`

使用HIBERNATE的DetachedCriteria无限级联取部份字段的查询结果集转换

阅读更多
--后记,本文所讲的实现在我的另一篇博客中提供了完整的源代码与编译好的JAR包,可供下载使用。http://lgdlgd.iteye.com/admin/blogs/619370

有经验的朋友都知道,列表查询时,一般不会查这个实体的全部字段,取部份字段的DetachedCriteria查询,常常 会用到AliasToBeanResultTransformer类把结果转换成POJO:
criteria.setResultTransformer(Transformers.aliasToBean(pojoClass));

但很显然,AliasToBeanResultTransformer类对结果集的封装是很简陋的,只能对本POJO的属性进行封装。
比如USER表有id,name,email,organization(组织,另一个实体),如果查询USER时要包含organization实体的name属性,但不要整个oganization,AliasToBeanResultTransformer就无能为力了,我写了另一个结果转换器来完成这个功能,其中用到OGNL工具包,Struts2和WEBWORK就是使用它把前台页面传回的参数填充ACTION里的属性对象的,我的灵感也是来源于此。

查询方法代码:


////////SERVICE中查询的方法
public List<User> findUsers(){
//要查的部份字段,ID,用户名和所在组织的名称
String[] columns = {"id","name","organization.name"};
//构造查询器
DetachedCriteria criteria = DetachedCriteria.forClass(User.class,"_user");
//筛选字段
DetachedCriteriaUntils.selectColumn(criteria,columns,Class pojoClass, String rootAlias,boolean forJoinTable);
//查询数据库
return userDAO.findByCriteria(criteria)
}


查询方法结束,下面这个方法就是如何筛选字段,不过只是部份代码,还有几个方法没有全列出,重点看最后几行

/**
	 * 该方法提供DetachedCriteria对查询字段的封装,
	 * 可支持无限级联取部分字段  ser.organization.parentOrganization.parentOrganization.orgName
	 * 但请注意1点 ,连接采用内联,级联越多,结果集可能就越少;
	 * @param columnNames
	 *            字符串数组,以数据的形式接收要查询的字段属性,如String[] column={"属性1","属性2","属性3"};
	 * @param pojoClass
	 *            实体类的Class,如Mobile.class;
	 * @param rootAials
	 *            为要查询的POJO对象指定一个别名
	 * @return DetachedCriteria 的一个对象,如果需要查询条件,在些对象后追加查询条件。
	 * 
	 * @param forJoinTable 是否多表连接查询
	 */
	public static void selectColumn(DetachedCriteria criteria, String[] columnNames,
			Class<? extends BaseModel> pojoClass, String rootAlias,boolean forJoinTable) {
		if (null == columnNames) {
			return;
		}
	
		//使用这个临时变量集合,是因为dinstinct关键字要放在最前面,而distinct关键字要在下面才决定放不放,
		List<Projection> tempProjectionList = new ArrayList<Projection>();;
		
		Set<String> aliases = getAliasFromRequest();
		boolean hasJoniTable = false;
		for (String property : columnNames) {
			if(property.contains(POINT)){
				String[] propertyChain = property.split("\\.");
				if(aliases==null){aliases=new HashSet<String>(3);}
				createAlias(criteria,rootAlias,aliases,propertyChain,0);
				tempProjectionList.add(Projections.property(getAliasFromPropertyChainString(property)).as(property));
				hasJoniTable = true;
			}else{
				tempProjectionList.add(Projections.property(rootAlias + POINT + property).as(property));
			}
		}
		
		ProjectionList projectionList = Projections.projectionList();
		if(hasJoniTable || forJoinTable ||  getHasJoinTatleFromRequest()){//这个一定要放在tempProjectionList的前面,因为distinct要在最前面
			projectionList.add(Projections.distinct(Projections.id()));
		}
		
		for (Projection proj : tempProjectionList) {
			projectionList.add(proj);
		}
		
		criteria.setProjection(projectionList);
		
		if(!hasJoniTable){
			criteria.setResultTransformer(Transformers.aliasToBean(pojoClass));
		}else{//注意,重点就在这里,这里检测到如果有类类似于user.organization.name这种字段,就表示是级联其它表的部份字段,那么要使用下面的这个EscAliasToBean来转换结果集,而不能使用上面那个HIBERNATE提供的类
			criteria.setResultTransformer(new EscAliasToBean(pojoClass));
		}
	}



      本文主要不是讨论如何筛选字段,所以上面有部份方法没有再贴出来。下面是EscAliasToBean类的完整代码


package com.lgdlgd.hibernate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ognl.Ognl;

import org.hibernate.HibernateException;
import org.hibernate.transform.ResultTransformer;

import com.opensymphony.xwork2.util.InstantiatingNullHandler;
import com.opensymphony.xwork2.util.OgnlUtil;

/**
 * 支持属性为自定义对象的结果集转换的部份属性查询
 */
@SuppressWarnings({"serial","unchecked"})
public class EscAliasToBean implements ResultTransformer {

	private static final Map<String,Boolean> context = new HashMap<String,Boolean>(1);
	static{
		context.put(InstantiatingNullHandler.CREATE_NULL_OBJECTS, true);
	}
	
	/** POJO的class */
	private final Class resultClass;

	
	public EscAliasToBean(Class pojoClass) {
		if(pojoClass==null) throw new IllegalArgumentException("resultClass cannot be null");
		this.resultClass = pojoClass;		
	}

	@Override
	public List transformList(List collection) {
		return collection;
	}

	/**
	 * 结果集转换
	 * @param tuple 属性值集合
	 * @param aliases 属性名集合,就是上面的{id,name,organization.name};
	 * @return 单个POJO实例--查询结果
	 */
	@Override
	public Object transformTuple(Object[] tuple, String[] aliases) {
		try {
			Object root = resultClass.newInstance();
			for (int i = 0; i < aliases.length; i++) {
				Ognl.setValue(OgnlUtil.compile(aliases[i]), context, root, tuple[i]);
			}
			return root;
		} catch (Exception e) {
			throw new HibernateException(e.getMessage(),e);
		}
	}

}




到此完成,结果的转换比HIBERNATE的那个原生转换器还要简洁,本来这个类完全可以代替它的,只不过这个用到的其它的框架,牵连太多,功能虽强大,但恐在简单环境里速度不如原来的好。如果有朋友想要探讨如何筛选字段的部份,也可以提出来。


5
0
分享到:
评论
10 楼 ming_fanglin 2010-04-22  
博主,我有hibernate方面的问题,能加下我的qq么  174725498
9 楼 xiaoyuqi00 2009-12-12  
首先谢谢您的解答。

第一次用ssh大型的项目,遇到的问题很多,

一:
1、先save(),此时没有进行数据的插入,
2、再调用list(),此时当插入的那条数据就无法查询出,该怎么解决?

二:
1、我们用的opensessioninview,事务交给spring处理,问题都出现了。当你调用fluse的时候数据已经进数据库,没办法在控制了,但有些数据用必须立即进数据库然后查出。

请加我QQ:290851952。本人真心求教。

8 楼 lgdlgd 2009-12-11  
1、如果你使用HIBERNATE进行插入,那么插入完了不需要查,原来的对象马上就可以用。
2、如果你通过查询的方法获得原来的对象,如下
Java代码
userDao.save(user);  
userDao.find("from User u where u.name='xiaoyuqi00'"); 

userDao.save(user);
userDao.find("from User u where u.name='xiaoyuqi00'");

这时在find执行前HIBERNATE会先执行插入,不需要你担心flush的问题,另外,如果在此事务未结束前,发生可回滚的异常,这个条数据依然会被回滚,不需要担心数据问题。

当然,这些都有个前提,我的项目是由SPRING来控制事务的,上术情况的事务也要交由外层来控制。
7 楼 xiaoyuqi00 2009-12-11  
关于hibernate和事务的问题。


、需要立即插入数据时,需要调用fluse方法,但是调用后,事务又无法控制?

、如果需要刚刚插入的数据怎么获得(没有调用fluse,数据库目前还没有数据)?


请指点下
6 楼 xiaoyuqi00 2009-12-07  
lgdlgd 写道
xiaoyuqi00 写道
博主,用的DetachedCriteria的createAlias()去关联查询

查询的是关联表的所有字段,我看了你的方法,有点麻烦,

我们也没用struts2,请问DetachedCriteria有没有像HQL那样,

直接设置你要查询哪些字段,他就帮你查询你要查询的。

我想他应该有吧,要不每次都要全查,不是很浪费效率

请问博主知道么?

我想知道,

关注下

DetachedCriteria取部份字段的方式就是下面这样的,据我所知,只有这一种
ProjectionList projectionList = Projections.projectionList();  
...  
criteria.setProjection(projectionList); 

关于你说的麻烦,我也同意,但是你要把这些麻烦的东西像我一样封装起来,别人使用的时候你下面这样
String[] columns = new String[]{property1,property2,property3.property4};
DetachedCriteria criteria = getDetachedCriteria(POJO.class,columns,alias);
DAO.findByCriteria(criteria);

那么用起来就不麻烦了,你觉得呢?而且你把这封装起来作为基础框架,以后如果你发现有更好的方法的时候,把框架改一改,然后你的业务逻辑也不需要改,这不挺好吗?



博主,我有些hibernate的问题,希望和您请教下,麻烦加我QQ:290851952 谢
5 楼 lgdlgd 2009-09-26  
xiaoyuqi00 写道
博主,用的DetachedCriteria的createAlias()去关联查询

查询的是关联表的所有字段,我看了你的方法,有点麻烦,

我们也没用struts2,请问DetachedCriteria有没有像HQL那样,

直接设置你要查询哪些字段,他就帮你查询你要查询的。

我想他应该有吧,要不每次都要全查,不是很浪费效率

请问博主知道么?

我想知道,

关注下

DetachedCriteria取部份字段的方式就是下面这样的,据我所知,只有这一种
ProjectionList projectionList = Projections.projectionList();  
...  
criteria.setProjection(projectionList); 

关于你说的麻烦,我也同意,但是你要把这些麻烦的东西像我一样封装起来,别人使用的时候你下面这样
String[] columns = new String[]{property1,property2,property3.property4};
DetachedCriteria criteria = getDetachedCriteria(POJO.class,columns,alias);
DAO.findByCriteria(criteria);

那么用起来就不麻烦了,你觉得呢?而且你把这封装起来作为基础框架,以后如果你发现有更好的方法的时候,把框架改一改,然后你的业务逻辑也不需要改,这不挺好吗?
4 楼 xiarilian12 2009-09-26  
xiaoyuqi00 写道
博主,用的DetachedCriteria的createAlias()去关联查询

查询的是关联表的所有字段,我看了你的方法,有点麻烦,

我们也没用struts2,请问DetachedCriteria有没有像HQL那样,

直接设置你要查询哪些字段,他就帮你查询你要查询的。

我想他应该有吧,要不每次都要全查,不是很浪费效率

请问博主知道么?

我想知道,

关注下

同问。。。
3 楼 xiaoyuqi00 2009-09-26  
博主,用的DetachedCriteria的createAlias()去关联查询

查询的是关联表的所有字段,我看了你的方法,有点麻烦,

我们也没用struts2,请问DetachedCriteria有没有像HQL那样,

直接设置你要查询哪些字段,他就帮你查询你要查询的。

我想他应该有吧,要不每次都要全查,不是很浪费效率

请问博主知道么?

我想知道,

关注下
2 楼 lgdlgd 2009-09-22  
部份字段查询时,你要用到以下代码

ProjectionList projectionList = Projections.projectionList(); 
....
取部份字段,并创建级联关系
....
criteria.setProjection(projectionList); 

用了这个代码,你必须使用下面的代码来将结果集转换成POJO的集合,否则返回的就是对象数组,当然如果你不在乎,愿意自已封装的话,你可以不要下面的代码,也就可以不需要用OGNL组件。
criteria.setResultTransformer(...));   

1 楼 黑猪王子 2009-09-22  
突然想到一个疑问了  为什么要重新组装结果Bean呢?   如果可以关联起来的Bean直接都是有getter的,返回回来的Bean里直接getter就可以了,不需要在ognl组装一个了吧?

我直接用for循环加别名实现的无限级关联

相关推荐

Global site tag (gtag.js) - Google Analytics