问题描述:
用户、角色、权限表中,用户多对多角色,角色多对多权限。
User Entity:
@ManyToMany(fetch = FetchType.EAGER) // 立即从数据库中进行加载数据;
@JoinTable(name = "sys_user_role", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns = {
@JoinColumn(name = "role_id") })
private List<SysRole> roles;// 一个用户具有多个角色
Role Entity:
// // 角色 -- 权限关系:多对多关系;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "sys_role_permission", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = {
@JoinColumn(name = "permission_id") })
private List<SysPermission> permissions;
// 用户 - 角色关系定义;
@ManyToMany
@JoinTable(name = "sys_user_role", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = {
@JoinColumn(name = "uid") })
private List<UserInfo> users;// 一个角色对应多个用户
Permission Entity:
@ManyToMany
@JoinTable(name = "sys_role_permission", joinColumns = {
@JoinColumn(name = "permission_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") })
private List<SysRole> roles;
执行shiro时报错:org.hibernate.HibernateException: 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所有关联对象的方式
如果这样写的情况下, 就会报cannot simultaneously fetch multiple bags异常,从SQL日志分析,hibernate是这样执行的
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实体列,所以就报上述异常。
当持久框架抓取一方的对象时,同时加载多方的对象放进容器中,多方又可能与关联其它对象,Hibernate实现的JPA,默认最高抓取深度含本身级为四级(它有个属性配置是0-3),若多方(第二级)存在重复值,则第三级中抓取的值就无法映射,按照这个道理,就应该报出无法同时加载多个包之异常。
解决方案:
1.将List变为Set2.fetch=FetchType.LAZY
3.@Fetch(FetchMode.SUBSELECT)
这种方法就是在不修改原结构注释的情况下,可以修改一下抓取策略,分开select实体,这样就不会出现重复列现象。
个人感觉还是不错的方法,打算就用它了。
4.@IndexColumn