lgdlgd 发表于 2013-1-23 02:28:01

基于HIBERNATE的全自动查询框架(二)

这个框架最重要的代码是自动拼装条件部份,本篇主要对拼装条件工具类入口类的代码进行讲解,首先是本框架的方法调用时序图
http://dl.iteye.com/upload/attachment/160363/7590ac48-afec-3e14-825d-abcb4a067497.png

下面是自动拼装条件工具类的入口
public List findByAutoConditionByLimit(Pagin pagin,Class<? extends BaseModel> pojoClass,String startChar,String[] columName,String[] excludeParameters,List<Criterion> customConditions, String alias){                  //框架的核心在这里,自动生成HIBERNATE的查询对象DetachedCriteria criteria = DetachedCriteriaUtil.createDetachedCriteria(pojoClass,startChar,excludeParameters,alias);.......//这里开始查询,根据查询对象封装的分页查询return findByDetachedCriteria(....);}

下面来看看DetachedCriteriaUtil工具类,这是自动封装查询条件的总入口,此工具首先根据POJO类和别名创建出DetachedCriteria对象,然后从请求中循环地分析出需要的查询参数,并格式化成字符串,然后通过“标装条件构造器”拼每个查询条件,最后拼装DetachedCriteria查询器中,本类主要的逻辑在于createDetachedCriteria和getCriterion方法,其它方法只要知道他们是干什么用的就行,代码:
package com.esc.common.util;import java.util.ArrayList;import java.util.Enumeration;import java.util.HashMap;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Set;import javax.servlet.http.HttpServletRequest;import org.hibernate.FetchMode;import org.hibernate.criterion.Criterion;import org.hibernate.criterion.DetachedCriteria;import org.hibernate.criterion.Expression;import org.hibernate.criterion.Projection;import org.hibernate.criterion.ProjectionList;import org.hibernate.criterion.Projections;import org.hibernate.transform.Transformers;import com.esc.common.baseclass.BaseModel;import com.esc.common.hibernate.resulttranformer.EscAliasToBean;import com.esc.common.util.conditionbuilder.ConditionBuilder;import com.esc.common.util.conditionbuilder.StandardConditionBuilder;/** * 拼装Hibernate条件 */public class DetachedCriteriaUtil {/** 小写字母X */public static final String MIDDLE_SEPRATOR_CHAR = "x";/** 两个空格 */public static final String SEPARATOR_TWO_SPACE = "";// 两个空格,避免和"日期// 时间"中的空格混淆/** 此字符串内容为" <= x <= ",不包括引号 */public static final String COMPLETE_SEPARATOR = SEPARATOR_TWO_SPACE+ Operator.LE + SEPARATOR_TWO_SPACE + MIDDLE_SEPRATOR_CHAR+ SEPARATOR_TWO_SPACE + Operator.LT + SEPARATOR_TWO_SPACE;private DetachedCriteriaUtil() {// do not allow to create Object}/** 标准条件构造器,核心中的核心 */private static final ConditionBuilder cb = new StandardConditionBuilder();/** * 自动拼装条件 ** @param pojoClazz *            实体类名 * @param startChar *            参数统一的开始字符 * @param alias *            别名 * @return */public static DetachedCriteria createDetachedCriteria(Class pojoClazz,String startChar, String alias) {return createDetachedCriteria(pojoClazz, startChar, null, alias);}/** * 自动拼装条件 2008-11-3 ** @param pojoClazz *            实体类名 * @param startChar *            参数前辍 * @param excludeParameters *            不作为查询条件的参数 * @param alias *            别名 * @return */public static DetachedCriteria createDetachedCriteria(Class pojoClazz,String startChar, String[] excludeParameters, String alias) {// 需要回传的参数,前台写成了name="userBean.sysUser.name|userBean.sysUser.realName"的参数,// 通过处理,可出使用第一个名字取回值Map<String, String> postBackParameters = null;// 这个字符串很重要,它将决定那些以它开头的参数被处理,其它不被处理String newStartChar = new StringBuilder(startChar).append(POINT).toString();// 创建DetachedCriteriaDetachedCriteria criteria = DetachedCriteria.forClass(pojoClazz, alias);// 取得当前请求HttpServletRequest request = SysContext.getRequest();// 取得所有请求参数Enumeration parameterNames = request.getParameterNames();// 创建alias集合,主要不为了不重复创建aliasSet<String> aliases = getAliasesFromRequest();// 查询条件是否包括当前正在处理的参数,如果检测到当前参数被排除了,设为FALSEboolean includeThisParameter = true;// 循环所有参数,拼装条件while (parameterNames != null && parameterNames.hasMoreElements()) {// 按顺序取得参数名String propName = parameterNames.nextElement().toString();// 取得参数值String[] propValue = request.getParameterValues(propName);// 只处理newStartChar开头的参数if (propName.startsWith(newStartChar)&& !CollectionUtils.isStringArrayEmpty(propValue)) {// 让下面的方法返回Criterion对象,在这里进行拼装if (propName.contains("|")) {// 组装很多ORString[] keys = propName.split("\\|");List<Criterion> criterions = new ArrayList<Criterion>(keys.length);for (String key : keys) {// getCriterion返回单个的Criterion对象criterions.add(getCriterion(key, startChar, propValue,criteria, cb, alias, pojoClazz, aliases));}addAndToCriteria(criteria, assembleOr(criterions));if (postBackParameters == null) {// 这里把请求中pojo.bb|pojo.cc参数换个EL表达式能接受的名,传回到REQUEST中,用于回显,AJAX查询的话这里就不需要了postBackParameters = new HashMap<String, String>(5);}postBackParameters.put(keys, StringUtil.getLinkString(propValue));} else {// 组装单个and// 要先判断此参数是否被用户排除在外了,includeThisParameter = true;if (excludeParameters != null&& excludeParameters.length > 0) {for (int i = 0; i < excludeParameters.length; i++) {if (propName != null&& propName.equals(excludeParameters)) {includeThisParameter = false;// 确实排除了}}}if (includeThisParameter)// 不被排除的参数名addAndToCriteria(criteria, getCriterion(propName,startChar, propValue, criteria, cb, alias,pojoClazz, aliases));}}}// 设置回传参数setPostBackParameter(postBackParameters);return criteria;}/** * 组装条件 ** @param propName *            带前辍的属性名 * @param startChar *            属性名的前辍 * @param propValue *            属性值 * @param criteria *            HIBERNATE条件对象 * @param cb *            条件生成器 * @param alias *            别名 * @param pojoClazz *            POJO类 * @return Criterion 条件对象 */private static Criterion getCriterion(String propName, String startChar,String[] propValue, DetachedCriteria criteria, ConditionBuilder cb,String alias, Class pojoClazz, Set<String> aliases) {//从pojo.aaa.bb参数名中,取出非前辍部份,即aaa.bbString key = propName.substring(startChar.length() + 1, propName .length());if (key.contains(POINT)) {// 如果还包含点号,即当前pojo属性中的属性,即级联了其它表进行查询//找个地方记住本次查询不是单表查询,而是级连了其它表的查询,最好的地方是放入请求setHasJoinTatleToRequest(SysContext.getRequest(), true);//选把别名创建好,HIBERNATE级联其它表进行查询时,对象属性要创建别名才行String[] keys = key.split("\\.");createAlias(criteria, alias, aliases, keys, 0);}if (propValue.length > 1) {// 一个属性有两个值,即JSP页面有两个输入框采用了同一个名字,如通过创建时间查询时,就需要输入区间了StringBuilder sb = new StringBuilder(30);//把这两个值拼成 aaa<=x<=bbb的字符串sb.append(propValue.trim()).append(COMPLETE_SEPARATOR).append(propValue);//通过标准条件构造器拼装return cb.parseToCriterion(key, sb.toString(), pojoClazz, alias);} else {// 一个属性单个值//通过标准条件构造器拼装return cb.parseToCriterion(key, propValue, pojoClazz, alias);}}private static final String ALIAS_KEY_IN_REQUEST = "ALIAS_KEY_IN_REQUEST";private static final String HAS_JOIN_TABLE_KEY_IN_REQUEST = "HAS_JOIN_TABLE_KEY_IN_REQUEST";private static void setAliasToRequest(HttpServletRequest request,Set<String> aliases) {request.setAttribute(ALIAS_KEY_IN_REQUEST, aliases);}public static Set<String> getAliasesFromRequest() {Set<String> aliases = (Set<String>) SysContext.getRequest().getAttribute(ALIAS_KEY_IN_REQUEST);if (aliases == null) {aliases = new HashSet<String>(5);setAliasToRequest(SysContext.getRequest(), aliases);}return aliases;}/** * 为类似于name="userBean.sysUser.name|userBean.sysUser.realName"的参数设置回传值 ** @param parameters */private static void setPostBackParameter(Map<String, String> parameters) {if (parameters != null) {Set<String> keySet = parameters.keySet();HttpServletRequest request = SysContext.getRequest();for (String key : keySet) {String[] keys = key.split("\\.");request.setAttribute(keys, parameters.get(key));}}}private static void setHasJoinTatleToRequest(HttpServletRequest request,boolean hasJoin) {request.setAttribute(HAS_JOIN_TABLE_KEY_IN_REQUEST, hasJoin);}private static boolean getHasJoinTatleFromRequest() {Boolean hasJoin = (Boolean) SysContext.getRequest().getAttribute(HAS_JOIN_TABLE_KEY_IN_REQUEST);return hasJoin == null ? false : hasJoin;}/** * 把集合里所有条件组装OR ** @param criterions *            待组装成OR的条件集合 * @return 组装好的条件 */public static Criterion assembleOr(List<Criterion> criterions) {if (criterions == null || criterions.size() == 0)return null;Criterion result = null;for (Criterion criterion : criterions) {if (criterion == null)continue;if (result == null) {result = criterion;} else {result = Expression.or(result, criterion);}}return result;}/** * 增加与条件 ** @param criteria * @param criterion */public static void addAndToCriteria(DetachedCriteria criteria,Criterion criterion) {if (criterion != null)criteria.add(criterion);}/** * 该方法提供DetachedCriteria对查询字段的封装可支持无限级联取部分字段,如取如下字段 * user.organization.parentOrganization.parentOrganization.orgName 但请注意1点 * ,连接采用内联,级联越多,结果集可能就越少; ** @param columnNames *            字符串数组,以数据的形式接收要查询的字段属性,如String[] column={"属性1","属性2","属性3"}; * @param pojoClass *            实体类的Class,如Mobile.class; * @param aials *            为要查询的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 = getAliasesFromRequest();boolean hasJoniTable = false;for (String property : columnNames) {if (property.contains(POINT)) {String[] propertyChain = property.split("\\.");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 {criteria.setResultTransformer(new EscAliasToBean(pojoClass));}}private static final String POINT = ".";/** * 创建别名 ** @param criteria * @param rootAlais * @param aliases * @param columns * @param currentStep */public static void createAlias(DetachedCriteria criteria, String rootAlais,Set<String> aliases, String[] columns) {if (columns == null) {return;}for (String column : columns) {createAlias(criteria, rootAlais, aliases, column.split("\\."), 0);}}/** * 创建别名 ** @param criteria * @param rootAlais * @param aliases * @param columns * @param currentStep */private static void createAlias(DetachedCriteria criteria,String rootAlais, Set<String> aliases, String[] columns,int currentStep) {if (currentStep < columns.length - 1) {if (!aliases.contains(converArrayToAlias(columns, currentStep))) {if (currentStep > 0) {criteria.createAlias(converArrayToAlias(columns, currentStep - 1)+ POINT + columns,converArrayToAlias(columns, currentStep)).setFetchMode(columns, FetchMode.JOIN);} else {criteria.createAlias(rootAlais + POINT + columns,converArrayToAlias(columns, currentStep)).setFetchMode(columns, FetchMode.JOIN);}aliases.add(converArrayToAlias(columns, currentStep));}currentStep++;createAlias(criteria, rootAlais, aliases, columns, currentStep);}}/** * 从"organization.parentOrganization.parentOrganization.parentOrganization.id" * 得到 * "organization_parentOrganization_parentOrganization_parentOrganization.id" ** @param property * @return */public static String getAliasFromPropertyChainString(String property) {if (property.contains(".")) {return property.substring(0, property.lastIndexOf(POINT)).replaceAll("\\.", "_")+ property.substring(property.lastIndexOf(POINT));}return property;}/** * 从数组中创建ALIAS ** @param columns * @param currentStep * @return */private static String converArrayToAlias(String[] columns, int currentStep) {StringBuilder alias = new StringBuilder();for (int i = 0; i <= currentStep; i++) {if (alias.length() > 0) {alias.append("_");}alias.append(columns);}return alias.toString();}}

下一篇分析标准条件构造器
页: [1]
查看完整版本: 基于HIBERNATE的全自动查询框架(二)