这个框架最重要的代码是自动拼装条件部份,本篇主要对拼装条件工具类入口类的代码进行讲解,首先是本框架的方法调用时序图
下面是自动拼装条件工具类的入口
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();
// 创建DetachedCriteria
DetachedCriteria criteria = DetachedCriteria.forClass(pojoClazz, alias);
// 取得当前请求
HttpServletRequest request = SysContext.getRequest();
// 取得所有请求参数
Enumeration parameterNames = request.getParameterNames();
// 创建alias集合,主要不为了不重复创建alias
Set<String> aliases = getAliasesFromRequest();
// 查询条件是否包括当前正在处理的参数,如果检测到当前参数被排除了,设为FALSE
boolean 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("|")) {// 组装很多OR
String[] 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[0], 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[i])) {
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.bb
String 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[0].trim()).append(COMPLETE_SEPARATOR).append(propValue[1]);
//通过标准条件构造器拼装
return cb.parseToCriterion(key, sb.toString(), pojoClazz, alias);
} else {// 一个属性单个值
//通过标准条件构造器拼装
return cb.parseToCriterion(key, propValue[0], 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[keys.length - 1], 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[currentStep],
converArrayToAlias(columns, currentStep))
.setFetchMode(columns[currentStep], FetchMode.JOIN);
} else {
criteria.createAlias(
rootAlais + POINT + columns[currentStep],
converArrayToAlias(columns, currentStep))
.setFetchMode(columns[currentStep], 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[i]);
}
return alias.toString();
}
}
下一篇分析标准条件构造器