Hibernate抓取策略以及如何避免cannot simultaneously fetch multiple bags异常
2015-06-18 10:25
274 查看
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代码
@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(name = "TB_PERSON_EVENT", joinColumns = {@JoinColumn(name = "PERSON_ID")},
Java代码
inverseJoinColumns = {@JoinColumn(name = "EVENT_ID")})
public List<Event> getEvents() {
return events;
}
@OneToMany(fetch=FetchType.EAGER)
@JoinTable(name = "TB_PERSON_EMAIL", joinColumns = {@JoinColumn(name = "PERSON_ID")},
Java代码
inverseJoinColumns = {@JoinColumn(name = "ID")})
ublic List<Email> getEmails() {
return emails;
如果这样写的情况下, 就会报cannot simultaneously fetch multiple bags异常,从SQL日志分析,hibernate是这样执行的
Java代码
select *
from TB_PERSON tp left outer join TB_PERSON_EMAIL tpe1 on tp.person_id = tpe1.person_id
left outer join TB_PERSON_EMAIL tpe2 on tpe1.id = tpe2.id
left outer join TB_PERSON_EVENT tpet on tp.person_id = tpet.person_id
left outer join TB_EVENT te on tpet.event_id = te.event_id
where tp.person_id = 2;
输出的记过,就会出现两个同样的email实体列,所以就报上述异常。
如果要避免这个异常,我在网上搜了许多资料都是说把fetch=FetchType.LAZY
变成懒加载模式,或者把List修改成Set集合,这两种我验证过了,是可以行的通的,但是如果不想采取这种两种办法,
怎么办。
其实还有一种,是看springside得到的一种方法,代码修改如下
Java代码
@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(name = "TB_PERSON_EVENT", joinColumns = {@JoinColumn(name = "PERSON_ID")},
Java代码
inverseJoinColumns = {@JoinColumn(name = "EVENT_ID")})
@Fetch(FetchMode.SUBSELECT)
public List<Event> getEvents() {
return events;
}
@OneToMany(fetch=FetchType.EAGER)
@JoinTable(name = "TB_PERSON_EMAIL", joinColumns = {@JoinColumn(name = "PERSON_ID")},
Java代码
inverseJoinColumns = {@JoinColumn(name = "ID")})
@Fetch(FetchMode.SUBSELECT)
public List<Email> getEmails() {
return emails;
}
这种方法就是在不修改原结构注释的情况下,可以修改一下抓取策略,分开select实体,这样就不会出现重复列现象。
个人感觉还是不错的方法,打算就用它了。
另外就是个人并不是太喜欢全抓取,密密麻麻一大堆的SQL打印的控制台上,排查SQL都麻烦,所以最终修改版,个人比较喜欢的注释方法:
Java代码
@ManyToMany(fetch=FetchType.LAZY)
@JoinTable(name = "TB_PERSON_EVENT", joinColumns = {@JoinColumn(name = "PERSON_ID")},
Java代码
inverseJoinColumns = {@JoinColumn(name = "EVENT_ID")})
@Fetch(FetchMode.SUBSELECT)
public List<Event> getEvents() {
return events;
}
@OneToMany(fetch=FetchType.LAZY)
@JoinTable(name = "TB_PERSON_EMAIL", joinColumns = {@JoinColumn(name = "PERSON_ID")},
Java代码
inverseJoinColumns = {@JoinColumn(name = "ID")})
@Fetch(FetchMode.SUBSELECT)
public List<Email> getEmails() {
return emails;
}
收工,就写到这里了,该吃饭喽。
分享到:
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代码
@ManyToMany(fetch=FetchType.LAZY)
修改为
Java代码
@ManyToMany(fetch=FetchType.EAGER)
其他内容保持不变,这样测试下来没有出现问题。
无需,测博主代码,表示正常无压力~!
5 楼 Mr.TianShu 2013-02-11 引用
Java代码
//Default: FetchType.EAGER 默认是即时抓取 做连接 如果,fetch=FetchType.LAZY 时private ChengJiDengJi chengJiDengJi;查找ChengJiDengJiFenShu 是直接查找不需要左连接 ChengJiDengJi
//@ManyToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY)//有设置延迟加载
//@JoinColumn(name="chengJiDengJi_id")
@ManyToOne(fetch=FetchType.LAZY,cascade = {CascadeType.ALL})
@JoinColumn(name="chengJiDengJi_id", nullable = false, updatable = false, insertable = false)
public ChengJiDengJi getChengJiDengJi()
{
return chengJiDengJi;
}
//Default: FetchType.LAZY 默认是延迟抓取 不需要左联接 如果是FetchType.EAGER 即时抓取 就会是左联接查询
//@JoinTable(name = "STUDENT_PHONE", joinColumns = { @JoinColumn(name = "STUDENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "PHONE_ID") })
//@OneToMany(mappedBy="chengJiDengJi",cascade={CascadeType.ALL})
//@org.hibernate.annotations.IndexColumn(name = "chengJiDengJiFenShu_position", nullable = false, base = 1)
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@JoinColumn (name = "chengJiDengJi_id", nullable = false)
@Fetch(FetchMode.SUBSELECT)
public List<ChengJiDengJiFenShu> getChengJiDengJiFenShu()
{
return chengJiDengJiFenShus;
}
遇到情况,加上这东西也不报错了
Java代码
//@org.hibernate.annotations.IndexColumn(name = "chengJiDengJiFenShu_position", nullable = false, base = 1)
4 楼 buffering 2012-08-07 引用
给力,很给力!
3 楼 NSCoffee 2012-03-06 引用
方法确实不错,不过测试一下貌似最后给出的配置有点问题
Java代码
@ManyToMany(fetch=FetchType.LAZY)
修改为
Java代码
@ManyToMany(fetch=FetchType.EAGER)
其他内容保持不变,这样测试下来没有出现问题。
2 楼 csheaven01 2011-12-12 引用
好强。。。直接解决问题
1 楼 tsinglongwu 2011-06-17 引用
啥也不说了,太给力了。。。
相关文章推荐
- 插件五之滚动条jquery.slimscroll.js
- Jquery各Ajax函数:$.get(),$.post(),$.ajax(),$.getJSON() ~~~~~(转自残剑博客园)
- 前端框架
- DataTable转json字符串,jQuery.parseJSON()把json字符串转为标准的json对象格式
- 初步认识JavaScript函数库jQuery
- jQuery全屏滚动插件fullPage.js
- 将本地html文件拖到IE8浏览器无法打开,直接弹出一个下载的对话框
- HTML5 section、article和div区别
- Jackson 框架,轻易转换JSON
- jQuery遮罩插件jQuery.blockUI.js简介
- CSS代码使任何元素垂直居中
- Dreamweaver - <!DOCTYPE html>
- javascript从url中获取请求参数
- 用JavaScript显示浏览器客户端信息的超相近教程
- Json转换利器Gson之实例一-简单对象转化和带泛型的List转化
- JavaScript/jQuery 表单美化插件小结
- 递归例子(Javascript)
- 插件二之页面加载进度条pace.js
- 浅谈Jquery核心函数
- bootstrap(2)