使用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)
在调试时查看hibernate的代码(AbstractPersistentCollection.java),我发现:
1)在“listOfX”成员初始化之前调用我的@PostLoad方法 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()访问器中,添加一个synchronized块。但是,我想避免这种情况。
那么,有没有办法在调用@PostLoad之前执行急切的抓取?我不知道有这样做的JPA设施,所以我希望有一些我不知道的事情。
另外,也许Hibernate的专有API可以控制这种行为?
答案 0 :(得分:2)
这可能为时已晚,但hibernate似乎不支持默认的jpa fetchtype选项
@OneToMany(orphanRemoval = false, fetch = FetchType.EAGER)
您必须使用特定于休眠的:
@LazyCollection(LazyCollectionOption.FALSE)
答案 1 :(得分:0)
我不知道如何解决这个问题,但我认为稍微重构可能有所帮助,我的想法是将代码移到@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();
}
}
一旦完成初始化bean的所有容器服务,服务器就会调用PostConstruct。
答案 2 :(得分:0)