EclipseLink Lury Property Throwing ConcurrencyException - PullRequest
0 голосов
/ 24 апреля 2019

У меня есть следующая схема (упрощенная и имена изменены):

@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
}

По сути, Foo расширяет Fubar, что имеет свойство Bar, загруженное лениво. Как показывает мой комментарий в коде, FetchType.LAZY был недавно добавлен. Теперь мы получаем ConcurrencyException от EclipseLink при простом вызове метода getBar после извлечения записи Foo из базы данных.

@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
  }
}

Вот упрощенная версия FooDao. Мы используем нечто очень похожее (но не идентичное) на EntityManagerHelper, описанный в другом посте (по сути просто EnityManager внутри ThreadLocal). Для простоты я просто буду использовать EntityManagerHelper в коде ниже:

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;
  }
}

И последнее, но не менее важное: вот трассировка стека, которую мы получаем от 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)

В соответствии с EclipseLink FAQ по устранению взаимоблокировок разработчикам рекомендуется выполнять все отношения LAZY. Почему мы сейчас получаем ConcurrencyException? Мне нужна помощь эксперта EclipseLink по этому вопросу. Спасибо!

...