
处理关联关系是ORM中一常见操作,特别是在查询的时候,经常要在查询某个实体的时候要把它实体关联属性也查询出来,例如查询用户时级联查询角色信息,还有可能角色及联查询权限信息。在hibernate中实现这个目的有很多总方式:
- 配置
OpenSessionInViewFilter
,让Session
在View层中保存打开状态,可以随时使用,这看起来是个一劳永逸的办法,但其也带来了一些问题, 至于会有什么样的问题百度会给你答案。 - 在映射实体时把关联属性设置
lazy="false"
,表示该关联关系不进行懒加载,这样该实体关联属性就会一起被查询出来,这种方式有时候是可行的,但最大的问题就是不灵活,一但配置lazy="false"
,有时候不想该实体关联属性一起被查询出来就无能为力了。 - 在hql中提供了一个
"fetch"
关键字,其字面意义就是"抓取",利用它可以灵活地抓取我们想要查询实体关联属性。例如:查询用户抓取角色:FROM User u LEFT JOIN FETCH u.roles rs WHERE...
,如果角色还要抓取权限,则:FROM User u LEFT JOIN FETCH u.roles rs LEFT JOIN FETCH rs.privileges ps WHERE...
FETCH
使用起来的确很灵活,但在最近的一次使用中却碰见了一个问题,就是在得到的结果集中可能出现多条相同的记录,这是我们所不想要的,举个例子:
/** 论坛主题 **/public class Theme {/** 数据库ID **/private Integer id;/** 标题 **/private String title;/** 主题内容 **/private String content;/** 回复 **/private Set<Reply> replies = new HashSet<Reply>();//getter...//setter...}
/** 主题回复 **/public class Reply {/** 数据库id **/private Integer id;/** 回复内容 **/private String content;/** 回复所属主题 **/private Theme theme;//getter...//setter...}
为了简单起见,只是列出了示例属性,getter与setter方法也省略掉了。现在当我们查询Theme
的时候要抓取出Reply
,这时hql语句为:FROM Theme t LEFT JOIN FETCH t.replies rs WHERE t.id=?
,执行查询后得到Query
对象,这时你如果调用的是uniqueResult()
方法你将得到正确的结果,但是当你调用的是list()
方法,你会发现这个结果列表中很可能出现了多条重复的记录,确切的说是该Theme
有多少个Reply
就会有多少条重复的记录,这时你肯定会说调用uniqueResult()
方法不就完了吗?在这种本来就只有一条记录的情况肯定是没有问题的,但有时候我们想返回的就是多条记录呢?比如,根据Theme
的title属性进行模糊匹配(不考虑全文索引)的时候,这时hql语句为:FROM Theme t LEFT JOIN FETCH t.replies rs WHERE t.title like ?
,我们要返回的就是一个List
列表,这时只能调用Query.list()
方法,这样势必会出现多条重复记录。那么如何解决呢?
- 加上
DISTINCT
关键字,hql变为:FROM DISTINCT Theme t LEFT JOIN FETCH t.replies rs WHERE t.title like ?
,这样一般情况下 能返回确定的结果,但有时候也不行。例如,在Oracle中当有字段为clob,blob类型时就行不通了,因为数据根本不知道对于clob,blob类型的数据到底是不是相等,其它数据库就没有试过了,我估计也不行。所以DISTINCT
关键字也不是万能的。 - 在返回含有相同记录的
List
中,手工进行排除掉相同的记录,但这会增加额外的工作量,大家肯定也不愿干。 - 在调用list()方法之前,调用
query.setFirstIndex(0).setMaxResult(Integer.MAX_VALUE);
这样一个分页语句肯定是获取出了全部的记录 这样虽然能获取出正确的结果,但你会发现和没有设置分页执行的SQL语句是一样的,这就说明在这种情况下hibernate是先获取出了全部符合条件的记录然后再进行分页的。当你不需要分页的情况下执行是没有问题的,如果你又想分页又想级联抓取就会获取出不必要的记录,因为它是在内存中分页的。而且这样hibernate还会报一个警告:WARNING: firstResult/maxResults specified with collection fetch; applying in memory!
大致意思说的就是在内存中进行分页,这样肯定有性能方面的问题。
在相比之下,个人觉得还是第3种解决方案是最合适的(如果还有更好的解决方案请不吝赐教),因为只要不进行分页都是可以满足要求的,如果要进行分页只能不进行级联抓取了,要获取实体关联属性另外写查询语句。
--------------- END ---------------
每一篇文章都是小编精心为您准备的,写文不易,费时费脑;『分享』+『点赞』+『在看』三连击是小编坚持的最大动力,拜谢!




