`

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

阅读更多
欢迎加入阿里,有兴趣的发邮件给我fuqu.lgd@alibaba-inc.com, java技术体系好就行,具体要求不再这里发,怕屏蔽

这个框架最重要的代码是自动拼装条件部份,本篇主要对拼装条件工具类入口类的代码进行讲解,首先是本框架的方法调用时序图


下面是自动拼装条件工具类的入口
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();
	}
}



下一篇分析标准条件构造器
  • 大小: 159.6 KB
2
0
分享到:
评论
3 楼 IAmMrLi 2016-03-15  
2 楼 ganbo 2015-11-25  
1 楼 ganbo 2015-11-25  

相关推荐

    基于hibernate,spring,struts,shiro,activiti,easyui框架的物流系统,可做毕业设计

    在这个物流系统中,Hibernate用于将业务对象(如订单、货物、客户等)映射到数据库表,通过SQL查询操作数据,而无需直接编写大量的SQL语句,提高了开发效率和代码的可维护性。 2. **Spring**:Spring是Java企业级...

    基于Hibernate和SpringMVC框架的小型书店管理网站S

    【标题】:基于Hibernate和SpringMVC框架的小型书店管理网站S 【描述】:小型书店管理网站是一个常见的Web应用程序示例,它利用了Hibernate和SpringMVC这两个强大的开源框架。Hibernate是Java领域中广泛使用的对象...

    MyEclipse10+Struts2.1+Spring3.3+Hibernate3.3全注解框架搭建

    本教程将详细阐述如何利用MyEclipse10,一个强大的Java EE集成开发环境,搭建一个基于Struts2.1、Spring3.3和Hibernate3.3的全注解框架。这种框架被称为SSH2,因为它包含了Struts2、Spring和Hibernate这三大主流Java...

    hibernate的文档最全版

    14. **第二代API(Hibernate Annotations)**:Hibernate 4.0.0引入了基于Java注解的配置方式,使得代码更加简洁,减少了XML配置文件的依赖。 15. **Hibernate Tools**:包括Hibernate逆向工程工具,可以自动生成...

    基于hibernate,spring,spring mvc,bootstrap框架开发的管理系统

    《基于Hibernate,Spring,Spring MVC,Bootstrap框架的管理系统详解》 在现代企业级应用开发中,选择合适的框架能够显著提高开发效率,降低维护成本。本文将深入探讨一个基于Hibernate、Spring、Spring MVC以及...

    基于Struts+Hibernate的网上问卷调查系统

    **基于Struts+Hibernate的网上问卷调查系统** 在IT领域,构建一个网上问卷调查系统是一项常见的任务,它能够帮助企业、研究机构或者个人收集用户数据,进行统计分析。本项目采用Struts2作为前端MVC框架,Hibernate...

    hibernate相关插件全集

    此外,它还可以生成基于Hibernate的DAO(数据访问对象)层,进一步提高了开发效率。 接下来是hibernate-annotations-3.3.0.GA,这是Hibernate 3.x系列的一个版本,包含了Hibernate的注解支持。在Java中,注解是一种...

    基于spring+springmvc+hibernate的全注解开发

    基于Spring+SpringMVC+Hibernate的全注解开发,允许开发者摆脱传统的XML配置文件,转而使用注解来实现组件的配置,大大简化了开发流程,提高了开发效率。 **Spring框架** 是一个全面的后端应用管理框架,它提供了...

    springmvc +spring+hibernate的smart框架

    Smart框架是一种基于SpringMVC、Spring和Hibernate的整合框架,旨在简化企业级Web应用的开发工作。这个框架利用了Maven进行项目管理和构建,确保了依赖管理的有效性和一致性。通过集成这些主流的技术栈,Smart提供了...

    spring、spring mvc、hibernate、material 前端框架开发简易BBS论坛系统

    本项目是一个基于Spring、Spring MVC、Hibernate和Material前端框架构建的简单BBS论坛系统,旨在为初学者提供一个学习和实践这些技术的平台。在这个系统中,你可以看到如何将Java后端技术和现代化的前端设计结合,以...

    jsp+Struts+hibernate基本框架搭建demo

    总的来说,"jsp+Struts+hibernate基本框架搭建demo"是一个典型的Java Web应用开发实践,涵盖了前端展示、后端控制和数据库操作的全过程。通过这样的示例,开发者可以学习到如何在实际项目中有效地组织代码和配置,...

    基于全注解的SSH2框架

    **基于全注解的SSH2框架详解** SSH2框架,即Spring、Struts2和Hibernate的集成,是Java开发Web应用程序的主流框架之一。在这个基于全注解的SSH2框架中,我们将深入探讨如何利用注解来简化配置,提高开发效率,并...

    OA项目struts2,spring,hibernate 三大框架

    以下将详细阐述这三个框架的核心功能和它们在OA(办公自动化)项目中的应用。 **Struts2框架** Struts2是一个基于MVC(Model-View-Controller)设计模式的Web应用框架,它主要负责控制层。Struts2通过拦截器机制...

    jeesite web框架hibernate+spring+shiro

    **二、Spring:全方位的应用程序框架** Spring是Java领域中最为广泛应用的框架之一,它提供了依赖注入(DI)、面向切面编程(AOP)、MVC、数据访问/集成、事务管理等多种功能。在Jeesite中,Spring扮演着核心的角色...

    使用Hibernate开发租房系统78.

    查询房源则可以使用`Query`或`Criteria` API,通过HQL(Hibernate Query Language)或SQL编写查询语句。 进一步地,租房系统可能需要处理复杂的业务逻辑,比如租赁合同的签订和管理。这可能涉及到多个实体间的关联...

    Struts、Spring、Hibernate三大框架的原理和优点

    - **自动化的持久化操作**:Hibernate能够自动生成大部分SQL语句,大大减轻了开发者的负担。 - **支持多种数据库**:Hibernate支持多种关系型数据库,包括MySQL、Oracle、SQL Server等,使得迁移变得容易。 - **强大...

    Hibernate泛型Dao

    【描述】"基于hibernate5 泛型Dao实例,下载后改一下数据库配置直接可以用",意味着这个压缩包文件提供了一个已经实现好的Hibernate5版本的泛型Dao示例项目。用户只需要根据自己的数据库环境修改相应的配置信息,就...

    Struts-Spring-Hibernate三大框架整合项目——人力资源管理系统

    Spring框架则是一个全方位的应用框架,它不仅提供了依赖注入(DI)和面向切面编程(AOP)的功能,还集成了大量的其他框架,如数据访问、事务管理等。在人力资源管理系统中,Spring作为整体的容器,管理着所有bean的...

Global site tag (gtag.js) - Google Analytics