暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

hibernate的fetch用法

Java精讲 2021-04-02
1152

处理关联关系是ORM中一常见操作,特别是在查询的时候,经常要在查询某个实体的时候要把它实体关联属性也查询出来,例如查询用户时级联查询角色信息,还有可能角色及联查询权限信息。在hibernate中实现这个目的有很多总方式:

  1. 配置OpenSessionInViewFilter
    ,让Session
    在View层中保存打开状态,可以随时使用,这看起来是个一劳永逸的办法,但其也带来了一些问题, 至于会有什么样的问题百度会给你答案。
  2. 在映射实体时把关联属性设置lazy="false"
    ,表示该关联关系不进行懒加载,这样该实体关联属性就会一起被查询出来,这种方式有时候是可行的,但最大的问题就是不灵活,一但配置lazy="false"
    ,有时候不想该实体关联属性一起被查询出来就无能为力了。
  3. 在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()
方法,这样势必会出现多条重复记录。那么如何解决呢?

  1. 加上DISTINCT
    关键字,hql变为:FROM DISTINCT Theme t LEFT JOIN FETCH t.replies rs WHERE t.title like ?
    ,这样一般情况下 能返回确定的结果,但有时候也不行。例如,在Oracle中当有字段为clob,blob类型时就行不通了,因为数据根本不知道对于clob,blob类型的数据到底是不是相等,其它数据库就没有试过了,我估计也不行。所以DISTINCT
    关键字也不是万能的。
  2. 在返回含有相同记录的List
    中,手工进行排除掉相同的记录,但这会增加额外的工作量,大家肯定也不愿干。
  3. 在调用list()方法之前,调用query.setFirstIndex(0).setMaxResult(Integer.MAX_VALUE);
    这样一个分页语句肯定是获取出了全部的记录 这样虽然能获取出正确的结果,但你会发现和没有设置分页执行的SQL语句是一样的,这就说明在这种情况下hibernate是先获取出了全部符合条件的记录然后再进行分页的。当你不需要分页的情况下执行是没有问题的,如果你又想分页又想级联抓取就会获取出不必要的记录,因为它是在内存中分页的。而且这样hibernate还会报一个警告:WARNING: firstResult/maxResults specified with collection fetch; applying in memory!
    大致意思说的就是在内存中进行分页,这样肯定有性能方面的问题。

在相比之下,个人觉得还是第3种解决方案是最合适的(如果还有更好的解决方案请不吝赐教),因为只要不进行分页都是可以满足要求的,如果要进行分页只能不进行级联抓取了,要获取实体关联属性另外写查询语句。

--------------- END ---------------

每一篇文章都是小编精心为您准备的,写文不易,费时费脑;​『分享』+『点赞』+『在看』三连击是小编坚持的最大动力,拜谢!

文章转载自Java精讲,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论