首页 > 代码库 > hibernate之fetch

hibernate之fetch

    处理关联关系是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种解决方案是最合适的(如果还有更好的解决方案请不吝赐教),因为只要不进行分页都是可以满足要求的,如果要进行分页只能不进行级联抓取了,要获取实体关联属性另外写查询语句。