--后记,本文所讲的实现在我的另一篇博客中提供了完整的源代码与编译好的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的那个原生转换器还要简洁,本来这个类完全可以代替它的,只不过这个用到的其它的框架,牵连太多,功能虽强大,但恐在简单环境里速度不如原来的好。如果有朋友想要探讨如何筛选字段的部份,也可以提出来。