I have the following schema (simplified and names changed):
@Entity
@Table(name = "fubar")
@Inheritance(strategy = InheritanceType.JOINED)
public class Fubar {
@ManyToOne(fetch = FetchType.LAZY) // LAZY was recently added
@JoinColumn(name = "bar_id")
private Bar bar;
public Bar getBar() {
return bar;
}
}
@Entity
@Table(name = "foo")
public class Foo extends Fubar {
// Columns omitted for brevity
}
Basically, Foo
extends Fubar
, which has a lazily-loaded Bar
property. As my comment in the code indicates, FetchType.LAZY
was recently added. Now we are getting a ConcurrencyException
from EclipseLink when simply calling the getBar
method after retrieving a Foo
record from the database.
@Named
@RequestScoped
public class FooBean {
@Inject
private FooDao fooDao;
@PostConstruct
public void initialize() {
long fooId = getSelectedFooId();
Foo foo = fooDao.findById(fooId);
foo.getBar(); // Boom! -> ConcurrencyException
}
}
Here's a simplified version of the FooDao
. We are using something very similar (but not identical) to the EntityManagerHelper
described in another post (essentially just EnityManager
inside of ThreadLocal
). For simplicity, I'll just use the EntityManagerHelper
in the code below:
public class FooDao {
public Foo findById(long id) {
EntityManager em = EntityManagerHelper.getEntityManager();
List<Foo> foos = em
.createQuery("SELECT f FROM Foo f WHERE f.id = :id", Foo.class)
.setParameter("id", id)
.setMaxResults(1)
.getResultList();
return (foos.size() > 0) ? foos.get(0) : null;
}
}
Last, but not least, here's the stacktrace we are getting from EclipseLink:
org.eclipse.persistence.exceptions.ConcurrencyException:
Exception Description: A signal was attempted before wait() on ConcurrencyManager. This normally means that an attempt was made to
commit or rollback a transaction before it was started, or to rollback a transaction twice.
at org.eclipse.persistence.exceptions.ConcurrencyException.signalAttemptedBeforeWait(ConcurrencyException.java:84) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.helper.ConcurrencyManager.releaseReadLock(ConcurrencyManager.java:468) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.identitymaps.CacheKey.releaseReadLock(CacheKey.java:468) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.cloneAndRegisterObject(UnitOfWorkImpl.java:1041) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.cloneAndRegisterObject(UnitOfWorkImpl.java:955) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.sessions.UnitOfWorkIdentityMapAccessor.getAndCloneCacheKeyFromParent(UnitOfWorkIdentityMapAccessor.java:209) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.sessions.UnitOfWorkIdentityMapAccessor.getFromIdentityMap(UnitOfWorkIdentityMapAccessor.java:137) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerExistingObject(UnitOfWorkImpl.java:3942) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.queries.ObjectBuildingQuery.registerIndividualResult(ObjectBuildingQuery.java:448) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.queries.ReadObjectQuery.registerResultInUnitOfWork(ReadObjectQuery.java:887) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkEarlyReturn(ObjectLevelReadQuery.java:880) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.mappings.OneToOneMapping.checkCacheForBatchKey(OneToOneMapping.java:835) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.mappings.ForeignReferenceMapping.extractResultFromBatchQuery(ForeignReferenceMapping.java:531) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.indirection.BatchValueHolder.instantiate(BatchValueHolder.java:58) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.indirection.QueryBasedValueHolder.instantiate(QueryBasedValueHolder.java:116) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.indirection.DatabaseValueHolder.getValue(DatabaseValueHolder.java:89) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiateImpl(UnitOfWorkValueHolder.java:173) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiate(UnitOfWorkValueHolder.java:234) ~[org.eclipse.persistence.core.jar:na]
at org.eclipse.persistence.internal.indirection.DatabaseValueHolder.getValue(DatabaseValueHolder.java:89) ~[org.eclipse.persistence.core.jar:na]
at com.mycompany.model.Foo._persistence_get_bar(Foo.java)
at com.mycompany.model.Foo.getBar(Foo.java:16)
at com.mycompany.beans.FooBean.initialize(FooBean.java:20)
According to the EclipseLink FAQ for resolving deadlocks, developers are encouraged to make all relationships LAZY. Why are we now getting a ConcurrencyException
? I need an EclipseLink expert's help on this one. Thanks!