长时间读者,第一次问问,所以请温柔。 :)我正在使用Hibernate作为我的JPA 2.0提供程序( hibernate-core-4.1.16 , hibernate-jpa-1.0.1 )并且我遇到了麻烦看似简单的东西。
我有两个实体 A 和 B ,其中 A 包含 B 的列表。每次设置 B 的新列表时,我都想处理它。因此,我将处理放在列表的setter中。 Hibernate使用相同的setter。
这适用于从数据库加载 A 的实例,甚至在没有级联时刷新工作。但是,只要我启用 CascadeType.REFRESH 并进行刷新,访问setter中新的 B 列表就会导致臭名昭着的 LazyInitializationException 。
我尝试了一些事情(见下文),但没有任何帮助,我没有想法。 :(
我创建了一个最小的工作示例,以显示应该发生的事情以及实际发生的事情。
注意:我省略了id-attributes。 (如果这很重要:id-getters用@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
注释。)
@Entity
@Table(name = "tableA")
public class A {
private List<B> bar;
@OneToMany(mappedBy = "theA", cascade = CascadeType.ALL)
public List<B> getBar() {
return bar;
}
private void setBar(List<B> bar) {
this.bar = bar;
System.out.println("Number of Bs during set: " + bar.size());
}
}
类 B
@Entity
@Table(name = "tableB")
public class B {
private A theA;
@ManyToOne
@JoinColumn(name = "a_id", nullable = false)
public A getTheA() {
return theA;
}
private void setTheA(A theA) {
this.theA = theA;
}
}
两个表 tableA 和 tableB 每个都包含一行,因此将有一个 A 实例,其中包含乙
最后,这是执行的代码:
public static void main(String[] args) {
EntityManager em = Persistence.createEntityManagerFactory("TestDBManager").createEntityManager();
A theA = (A) em.createQuery("SELECT a FROM A a").getResultList().get(0);
System.out.println("Number of Bs after load: " + theA.getBar().size());
em.refresh(theA);
System.out.println("Number of Bs after refresh: " + theA.getBar().size());
}
这是预期的结果
Number of Bs during set: 1
Number of Bs after load: 1
Number of Bs during set: 1
Number of Bs after refresh: 1
但实际上会发生这种情况:
Number of Bs during set: 1
Number of Bs after load: 1
Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: Exception occurred inside setter of isi.power.core.test.A.bar
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:939)
at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:906)
at line of call to refresh
Caused by: org.hibernate.PropertyAccessException: Exception occurred inside setter of isi.power.core.test.A.bar
... many more
Caused by: java.lang.reflect.InvocationTargetException
... many more
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
... many more
如果我删除了对setter中列表的调用,Hibernate在调用setBar
之后就完成了它的魔法,当我稍后调用refresh
之后调用main
时,我没有异常。 1}})。但是我放弃了在setter中处理列表的可能性,我宁愿不这样做。
如果我没有级联刷新,即如果我替换
,问题就会消失cascade = CascadeType.ALL
与
cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE }
由于我主要是为了刷新而这样做,所以这不是一个选择。
我在fetch = FetchType.EAGER
- 注释中添加了@OneToMany
。同样的例外......
我使用em.getTransaction().begin();
和em.getTransaction().commit();
包围了加载和刷新来电。同样的例外......
我试图在setter中调用Hibernate.initialize
,导致另一个例外:
Number of Bs during set: 1
Number of Bs after load: 1
Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: Exception occurred inside setter of isi.power.core.test.A.bar
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:939)
at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:906)
at line of call to refresh
Caused by: org.hibernate.PropertyAccessException: Exception occurred inside setter of isi.power.core.test.A.bar
... many more
Caused by: java.lang.reflect.InvocationTargetException
... many more
Caused by: org.hibernate.AssertionFailure: force initialize loading collection
... many more
我的想法很新鲜......我非常感谢能够解决这个问题的任何指针。
答案 0 :(得分:1)
如果可以接受,则可以使用字段访问(即 - 注释字段,而不是方法)。
E.g:
@Entity
@Table(name = "tableA")
public class A {
@OneToMany(mappedBy = "theA", cascade = CascadeType.ALL)
private List<B> bar;
public List<B> getBar() {
return bar;
}
private void setBar(List<B> bar) {
this.bar = bar;
System.out.println("Number of Bs during set: " + bar.size());
}
}
使用您的示例类准备一个简单的项目 - 它可以使用字段访问。
但如果在刷新过程中需要进行处理也,那么您可以查看使用 @PostLoad 方法,例如:
@PostLoad
public void processB() {
System.out.println("Number of Bs after load/refresh: " + this.bar.size());
}
完整示例将看起来像这样(这样,当使用代码手动调用setter并在实体的加载/刷新内自动调用时,它应该都可以工作):
@Entity
@Table(name = "tableA")
public class A {
@OneToMany(mappedBy = "theA", cascade = CascadeType.ALL)
private List<B> bar;
public List<B> getBar() {
return bar;
}
public void setBar(List<B> bar) {
this.bar = bar;
processBar();
}
@PostLoad
public void processBar() {
System.out.println("Number of Bs after load/refresh: " + this.bar.size());
}
}