Hibernate抓取策略以及如何避免cannot simultaneously fetch multiple bags异常

来源:互联网 发布:淘宝达人后如何直播 编辑:程序博客网 时间:2024/05/22 00:22

Hibernate抓取策略以及如何避免cannot simultaneously fetch multiple bags异常

    博客分类: 
  • hibernate
HibernateSQL 

在说解决cannot simultaneously fetch multiple bags异常之前,我先说下抓取策略

注解@Fetch(FetchMode.?)抓取策略有三种

1、FetchMode.JOIN(默认的抓取策略),采用外连接的形式,left outer join ... on

2、FetchMode.SELECT 会另外发送一条sql语句加载当前对象的关联实体

3、FetchMode.SUBSELECT 会另外发送一条select语句抓取前面查询到的所有实体对象的关联实体

通过Hibernate输出的SQL日志看成,个人感觉2、3的差别不是太大 ,都是另起select语句查询与当前某个实体相关联的其他实体。

下面是我写的一个简单DEMO,主要来验证抓取策略和异常的处理方法

表结构

人员表  tb_person

 

+-------------+

| Field       |

+-------------+

| person_id   |

| person_age  |

| person_name |

+-------------+

一个人员可以有多个邮箱,邮箱表,人-邮箱是一对多的关系tb_person_email

 

+-----------+

| Field     |

+-----------+

| id        |

| person_id |

| address   |

+-----------+

事件表,一个人可以有多个事件,一个事件也可有多个人处理,是多对多关系tb_event,tb_person_event

 

+-------------+

| Field       |

+-------------+

| event_id    |

| event_title |

| event_date  |

+-------------+

 

 

+-----------+

| Field     |

+-----------+

| event_id  |

| person_id |

+-----------+

 

要执行的方法是:搜索人,以及相关的事件和邮件。

person.java

第一种写法,采用默认join抓取策略,进行强制抓取person所有关联对象的方式

 

Java代码  收藏代码
  1. @ManyToMany(fetch=FetchType.EAGER)  
  2. @JoinTable(name = "TB_PERSON_EVENT", joinColumns = {@JoinColumn(name = "PERSON_ID")},   
Java代码  收藏代码
  1. inverseJoinColumns = {@JoinColumn(name = "EVENT_ID")})  
  2. public List<Event> getEvents() {  
  3.     return events;  
  4. }  
  5.   
  6.   
  7. @OneToMany(fetch=FetchType.EAGER)  
  8. @JoinTable(name = "TB_PERSON_EMAIL", joinColumns = {@JoinColumn(name = "PERSON_ID")},  
Java代码  收藏代码
  1. inverseJoinColumns = {@JoinColumn(name = "ID")})  
  2. ublic List<Email> getEmails() {  
  3. return emails;  

如果这样写的情况下, 就会报cannot simultaneously fetch multiple bags异常,从SQL日志分析,hibernate是这样执行的

 

Java代码  收藏代码
  1. select *  
  2. from TB_PERSON tp left outer join TB_PERSON_EMAIL tpe1 on tp.person_id = tpe1.person_id   
  3. left outer join TB_PERSON_EMAIL tpe2 on tpe1.id = tpe2.id   
  4. left outer join TB_PERSON_EVENT tpet on tp.person_id = tpet.person_id  
  5. left outer join TB_EVENT te on tpet.event_id = te.event_id  
  6. where tp.person_id = 2;  

 输出的记过,就会出现两个同样的email实体列,所以就报上述异常。


如果要避免这个异常,我在网上搜了许多资料都是说把fetch=FetchType.LAZY

变成懒加载模式,或者把List修改成Set集合,这两种我验证过了,是可以行的通的,但是如果不想采取这种两种办法,

怎么办。


其实还有一种,是看springside得到的一种方法,代码修改如下

 

Java代码  收藏代码
  1. @ManyToMany(fetch=FetchType.EAGER)  
  2. @JoinTable(name = "TB_PERSON_EVENT", joinColumns = {@JoinColumn(name = "PERSON_ID")},  
Java代码  收藏代码
  1.  inverseJoinColumns = {@JoinColumn(name = "EVENT_ID")})  
  2. @Fetch(FetchMode.SUBSELECT)  
  3. public List<Event> getEvents() {  
  4.     return events;  
  5. }  
  6.   
  7. @OneToMany(fetch=FetchType.EAGER)  
  8. @JoinTable(name = "TB_PERSON_EMAIL", joinColumns = {@JoinColumn(name = "PERSON_ID")},  
Java代码  收藏代码
  1.  inverseJoinColumns = {@JoinColumn(name = "ID")})  
  2. @Fetch(FetchMode.SUBSELECT)  
  3. public List<Email> getEmails() {  
  4.         return emails;  
  5. }  

 这种方法就是在不修改原结构注释的情况下,可以修改一下抓取策略,分开select实体,这样就不会出现重复列现象。

个人感觉还是不错的方法,打算就用它了。


另外就是个人并不是太喜欢全抓取,密密麻麻一大堆的SQL打印的控制台上,排查SQL都麻烦,所以最终修改版,个人比较喜欢的注释方法:

 

Java代码  收藏代码
  1. @ManyToMany(fetch=FetchType.LAZY)  
  2. @JoinTable(name = "TB_PERSON_EVENT", joinColumns = {@JoinColumn(name = "PERSON_ID")},  
Java代码  收藏代码
  1.  inverseJoinColumns = {@JoinColumn(name = "EVENT_ID")})  
  2. @Fetch(FetchMode.SUBSELECT)  
  3. public List<Event> getEvents() {  
  4.     return events;  
  5. }  
  6.   
  7. @OneToMany(fetch=FetchType.LAZY)  
  8. @JoinTable(name = "TB_PERSON_EMAIL", joinColumns = {@JoinColumn(name = "PERSON_ID")},  
Java代码  收藏代码
  1.  inverseJoinColumns = {@JoinColumn(name = "ID")})  
  2. @Fetch(FetchMode.SUBSELECT)  
  3. public List<Email> getEmails() {  
  4.         return emails;  
  5. }  

收工,就写到这里了,该吃饭喽。

 

 

 

 

分享到:  
JAVA事务,JTA,JDBC,JDO,DAO,JNDI概念 | Hibernate Exception:An AnnotationConfigu ...
  • 2010-10-19 12:44
  • 浏览 7234
  • 评论(6)
  • 收藏
  • 分类:企业架构
  • 相关推荐
评论
6 楼 Mr.TianShu 2013-02-11   引用
NSCoffee 写道
方法确实不错,不过测试一下貌似最后给出的配置有点问题
Java代码  收藏代码
  1. @ManyToMany(fetch=FetchType.LAZY)    

修改为
Java代码  收藏代码
  1. @ManyToMany(fetch=FetchType.EAGER)   


其他内容保持不变,这样测试下来没有出现问题。




无需,测博主代码,表示正常无压力~!
5 楼 Mr.TianShu 2013-02-11   引用
Java代码  收藏代码
  1. //Default: FetchType.EAGER   默认是即时抓取   做连接 如果,fetch=FetchType.LAZY 时private ChengJiDengJi  chengJiDengJi;查找ChengJiDengJiFenShu 是直接查找不需要左连接 ChengJiDengJi  
  2.     //@ManyToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY)//有设置延迟加载  
  3.     //@JoinColumn(name="chengJiDengJi_id")  
  4.      @ManyToOne(fetch=FetchType.LAZY,cascade = {CascadeType.ALL})  
  5.      @JoinColumn(name="chengJiDengJi_id", nullable = false, updatable = false, insertable = false)  
  6.     public ChengJiDengJi getChengJiDengJi()  
  7.     {  
  8.         return chengJiDengJi;  
  9.     }  
  10.   
  11.   
  12.   
  13.   
  14.   
  15.     //Default: FetchType.LAZY   默认是延迟抓取   不需要左联接 如果是FetchType.EAGER     即时抓取 就会是左联接查询  
  16.     //@JoinTable(name = "STUDENT_PHONE", joinColumns = { @JoinColumn(name = "STUDENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "PHONE_ID") })  
  17.     //@OneToMany(mappedBy="chengJiDengJi",cascade={CascadeType.ALL})  
  18.        
  19.      //@org.hibernate.annotations.IndexColumn(name = "chengJiDengJiFenShu_position", nullable = false, base = 1)  
  20.      @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})  
  21.      @JoinColumn (name = "chengJiDengJi_id", nullable = false)  
  22.      @Fetch(FetchMode.SUBSELECT)  
  23.     public List<ChengJiDengJiFenShu> getChengJiDengJiFenShu()  
  24.     {  
  25.         return chengJiDengJiFenShus;  
  26.     }  
  27.       




遇到情况,加上这东西也不报错了
Java代码  收藏代码
  1. //@org.hibernate.annotations.IndexColumn(name = "chengJiDengJiFenShu_position", nullable = false, base = 1)  
4 楼 buffering 2012-08-07   引用
给力,很给力!
3 楼 NSCoffee 2012-03-06   引用
方法确实不错,不过测试一下貌似最后给出的配置有点问题
Java代码  收藏代码
  1. @ManyToMany(fetch=FetchType.LAZY)    

修改为
Java代码  收藏代码
  1. @ManyToMany(fetch=FetchType.EAGER)   


其他内容保持不变,这样测试下来没有出现问题。
2 楼 csheaven01 2011-12-12   引用
好强。。。直接解决问题
1 楼 tsinglongwu 2011-06-17   引用
啥也不说了,太给力了。。。
0 0