`
liudongbin521
  • 浏览: 25917 次
  • 性别: Icon_minigender_1
  • 来自: 黑龙江
社区版块
存档分类
最新评论

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

 
阅读更多

在说解决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")},
@ManyToMany(fetch=FetchType.EAGER)
@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. @OneToMany(fetch=FetchType.EAGER)
  6. @JoinTable(name = "TB_PERSON_EMAIL", joinColumns = {@JoinColumn(name = "PERSON_ID")},
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代码 复制代码 收藏代码
  1. inverseJoinColumns = {@JoinColumn(name = "ID")})
  2. ublic List<Email> getEmails() {
  3. return emails;
 inverseJoinColumns = {@JoinColumn(name = "ID")})
public List<Email> getEmails() {
	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;
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代码 复制代码 收藏代码
  1. @ManyToMany(fetch=FetchType.EAGER)
  2. @JoinTable(name = "TB_PERSON_EVENT", joinColumns = {@JoinColumn(name = "PERSON_ID")},
@ManyToMany(fetch=FetchType.EAGER)
@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. @OneToMany(fetch=FetchType.EAGER)
  7. @JoinTable(name = "TB_PERSON_EMAIL", joinColumns = {@JoinColumn(name = "PERSON_ID")},
 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代码 复制代码 收藏代码
  1. inverseJoinColumns = {@JoinColumn(name = "ID")})
  2. @Fetch(FetchMode.SUBSELECT)
  3. public List<Email> getEmails() {
  4. return emails;
  5. }
 inverseJoinColumns = {@JoinColumn(name = "ID")})
@Fetch(FetchMode.SUBSELECT)
public List<Email> getEmails() {
        return emails;
}

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

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

 

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

 

Java代码 复制代码 收藏代码
  1. @ManyToMany(fetch=FetchType.LAZY)
  2. @JoinTable(name = "TB_PERSON_EVENT", joinColumns = {@JoinColumn(name = "PERSON_ID")},
@ManyToMany(fetch=FetchType.LAZY)
@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. @OneToMany(fetch=FetchType.LAZY)
  7. @JoinTable(name = "TB_PERSON_EMAIL", joinColumns = {@JoinColumn(name = "PERSON_ID")},
 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代码 复制代码 收藏代码
  1. inverseJoinColumns = {@JoinColumn(name = "ID")})
  2. @Fetch(FetchMode.SUBSELECT)
  3. public List<Email> getEmails() {
  4. return emails;
  5. }
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics