Posted on: 2013-11-20, Last modified: 2015-07-31, View: 3111
使用Hibernate3.2的时候遇到这样的异常:
javax.persistence.PersistenceException: org.hibernate.HibernateException: cannot simultaneously fetch multiple bags at org.hibernate.ejb.Ejb3Configuration.createEntityManagerFactory(Ejb3Configuration.java:217) at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:114) ........
这个异常被org.hibernate.loader.BasicLoader抛出,它意思是hibernate不能在同一时间取出两个或者更多bags(例如:List,Collection对象)
问题原因
如果一个实体有多余一个的non-lazy关联将被视为一个bag(e.g., java.util.List or java.util.Collection properties annotated with @org.hibernate.annotations.CollectionOfElements or @OneToMany or @ManyToMany and not annotated with @org.hibernate.annotations.IndexColumn), Hibernate将会获取实体失败。
假设我们有个Parent类和两个child类,child1和child2, parent对每一个child类型有一个bag,如以下代码定义:
@OneToMany(mappedBy="parent",cascade=CascadeType.ALL, fetch=FetchType.EAGER) List<Child1> child1s = new LinkedList<Child1>(); @OneToMany(mappedBy="parent",cascade=CascadeType.ALL, fetch=FetchType.EAGER) List<Child2> child2s= new LinkedList<Child2>();
我们在每个关联上面定义了bag和EAGER, Hibernate会生成如下的sql语句:
select parent0_.id as p_id, parent0_.name as p_name, child1s1_.parent_id as c1_p_id, child1s1_.id as c1_id, child1s1_.id as c1_id_1, child1s1_.parent_id as c1_p_id_1, child1s1_.value as c1_val,child2s2_.parent_id as c2_p_id, child2s2_.id as c2_id, child2s2_.id as c2_id_, child2s2_.parent_id as c2_p_id_1, child2s2_.value as c2_val from PARENT parent0_ left outer join CHILD1 child1s1_ on parent0_.id=child1s1_.parent_id left outer join CHILD2 child2s2_ on parent0_.id=child2s2_.parent_id where parent0_.id=?
现在假设数据库表结构如下:
PARENT table --------------------------- ID NAME 122 PARENT-1 CHILD1 table ------------------------------ ID VALUE PARENT_ID 123 CHILD1-1 122 1 CHILD1-2 122 CHILD2 table ------------------------------ ID VALUE PARENT_ID 124 CHILD2-1 122
加入我们输入的id=122, 查询完成以后会输出这样的结果:
P_ID P_NAME C1_P_ID C1_ID C1_ID_1 C1_P_ID_1 C1_VAL C2_P_ID C2_ID C2_ID_ C2_P_ID_1 C2_VAL 122 Parent-1 122 123 123 122 CHILD1-1 122 124 124 122 CHILD2-1 122 Parent-1 122 1 1 122 CHILD1-2 122 124 124 122 CHILD2-1
解决方案
针对这个问题现在有三个解决方案:
1. 使用LAZY loading获得关联实体,不用把所有的关联关系都改成lazy, 只要保证在同一个实体上面没有多于一个的EAGER关联就可以。
2. 在关联关系上使用@IndexColumn标签:
@OneToMany(mappedBy="parent",cascade=CascadeType.ALL, fetch=FetchType.EAGER) @IndexColumn(name="INDEX_COL") List<Child1> child1s = new LinkedList<Child1>();
使用@IndexColumn可以使Hibernate在获取父对象时同时也获取关联对象的index, Hibernate可以借助index判断当前对象时候已经被加载过。
3. 第三个方法是使用java.util.Set 替代java.util.List和java.util.Collection。