Выполнить @PostLoad _after_ нетерпеливо выбирая? - PullRequest
11 голосов
/ 21 декабря 2010

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

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

Глядя на код hibernate (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 (), добавив синхронизированный блок. Однако я хочу этого избежать.

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

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

Ответы [ 3 ]

2 голосов
/ 19 февраля 2014

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

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

Вы должны использовать специфичный для hibernate:

@LazyCollection(LazyCollectionOption.FALSE)
0 голосов
/ 26 апреля 2019

Обновлена ​​ссылка на отчет об ошибке:

https://hibernate.atlassian.net/browse/HHH-6043

Это исправлено в 4.1.8 и 4.3.0 или новее

0 голосов
/ 22 декабря 2010

Я не знаю, как это исправить, но я думаю, что небольшой рефакторинг может помочь, идея состоит в том, чтобы переместить код в @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, как только завершит инициализацию всех служб контейнера для компонента.

...