Выполнить @PostLoad _after_ с нетерпением?

Используя JPA2/Hibernate, я создал объект A, который имеет однонаправленное сопоставление объекту X (см. ниже). Внутри A у меня также есть временный член "t", который я пытаюсь вычислить с помощью метода @PostLoad. Для вычисления требуется доступ к ассоциированной Xs:

@Entity  
public class A {  
    // ...
    @Transient
    int t;

    @OneToMany(orphanRemoval = false, fetch = FetchType.EAGER)  
    private List listOfX;  

    @PostLoad
    public void calculateT() {
        t = 0;
        for (X x : listOfX)
            t = t + x.someMethod();
    }
}

Однако, когда я пытаюсь загрузить этот объект, я получаю ошибку "org.hibernate.LazyInitializationException: незаконный доступ к загрузке коллекции".

 at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:363)
 at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
 at org.hibernate.collection.PersistentBag.get(PersistentBag.java:445)
 at java.util.Collections$UnmodifiableList.get(Collections.java:1154)
 at mypackage.A.calculateT(A.java:32)

Глядя на спящий код (AbstractPersistentCollection.java) во время отладки, я обнаружил, что:

1) Мой метод @PostLoad вызывается до того, как элемент "listOfX" инициализирован
2) Код Hibernate имеет явную проверку, чтобы предотвратить инициализацию коллекции с нетерпением из-за @PostLoad:

 protected final void initialize(boolean writing) {
  if (!initialized) {
   if (initializing) {
    throw new LazyInitializationException("illegal access to loading collection");
   }
   throwLazyInitializationExceptionIfNotConnected();
   session.initializeCollection(this, writing);
  }
 }

Единственный способ, которым я решил исправить это, - это прекратить использование @PostLoad и переместить код инициализации в getT() accessor, добавив синхронизированный блок. Тем не менее, я хочу этого избежать.

Итак, есть ли способ, с помощью которого можно было бы получать желаемые результаты перед вызовом @PostLoad? Я не знаю возможности JPA для этого, поэтому я надеюсь, что там что-то не знаю.

Кроме того, возможно, что у Hibernate API есть что-то, чтобы контролировать это поведение?

Ответ 2

Это может быть слишком поздно, но hibernate, похоже, не поддерживает параметр jpa fetchtype по умолчанию

@OneToMany(orphanRemoval = false, fetch = FetchType.EAGER)

Вы должны использовать определенный спящий режим:

@LazyCollection(LazyCollectionOption.FALSE)

Ответ 3

Я не знаю, как это исправить, но я думаю, что небольшой рефакторинг может помочь, идея состоит в том, чтобы переместить код в @PostConstruct

так, например, ваш класс:

@Entity  
public class A {  
    // ...
    @Transient
    int t;

@OneToMany(orphanRemoval = false, fetch = FetchType.EAGER)  
private List listOfX;  

@PostConstruct
public void calculateT() {
    t = 0;
    for (X x : listOfX)
        t = t + x.someMethod();
}

}

Сервер будет вызывать PostConstruct, как только он завершит инициализацию всех сервисов контейнера для bean.