`

基于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  

相关推荐

    基于JavaEE的快速开发框架

    Hibernate是后来又补上的,最早的版本没有是因为第一个版本是为游戏服务器架构的,唯快不破的准则放弃了Hibernate,而后面增加回来是基于后台管理功能的需要。这一过程纠正了我一个开发框架中只能存在一个ORM的想法...

    基于 JavaEE 的快速开发框架 Tephra-JavaEE

    Tephra旨在构建一个稳定、高效、易于集群、快速扩展的JavaEE开发框架。目前,Tephra已经具备了以下特性: 提供类级别的热更新,但仅建议在需要快速修正严重BUG、并且无法立即进行全更新时使用。 提供全冗余方式的...

    BeetlSQL数据库访问框架是一个全功能 DAO 工具,同时具有 Hibernate.rar

    BeetSql是一个全功能DAO工具,同时具有Hibernate 优点 & Mybatis优点功能,适用于承认以SQL为中心,同时又需求工具能自动能生成大量常用的SQL的应用。 在开发效率上,无需注解,自动使用大量内置SQL,轻易完成增删...

    基于SSH框架的BBS论坛JavaEE项目源码

    10、最近登录过(三天,一周、一个月、三个月、半年)查询 11、类似微信团队号(与用户沟通账户以及推送系统消息) 12、QQ登录 jeebbsV4.0修复以及完善部分 1.权限的访问的地址链接 2.图片太大显示不全问题 3....

    Java的学习之路,学习JavaEE以及框架时候的一些项目,结合博客和源码,让你受益匪浅,适合Java初学者和刚入门开始学框架者

    [Spring]基于Spring框架的Web应用演示(附带cglib工具进行动态代理) [Tomcat7.0]Tomcat7版本安装包 [UltraISO]制作U盘启动盘需要的 [log4j_jar]log4j的支持包 [myAutoLoginWeb]过滤器Filter学习-实现用户的自动...

    基于JavaWEB+SSM+mysql框架构建的在线商城系统源码+数据库+项目说明(课程设计).zip

    本项目实现了通用 Mapper,免写 SQL,全自动处理关联查询。通过合理配置 MyBatis Generator 和自定义插件,灵活隔离手写代码和自动生成代码。实现了 BaseService 类对 Service 层进行抽象。通过拦截器实现了方法级...

    OA办公自动化管理系统(Struts1.2+Hibernate3.0+Spring2+DWR).rar

    SpringBoot 毕业设计,SpringBoot 课程设计,基于SpringBoot+Vue开发的,含有代码注释,新手也可看懂。ssm整合开发,小程序毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:...

    SSM整合.zip

    mybatis它是轻量级持久层框架,由ibatis演化而来。它完成将数据库的结果集封装到对象中POJO。业务层控制层和使用Hibernate框架一样。Hibernate基于hql是完全...全自动ORM。Mybatis基于sql是半面向对象。半自动的ORM。

    基于SSH的自助纳税申报系统【项目源码+数据库脚本】(毕设)

    ​后台框架:Spring、Struts2、Hibernate ​数据库:MySQL 开发环境:JDK、Eclipse、Tomcat 三、系统功能 本自动纳税申报系统将功能模块分为管理员功能模块和用户功能模块两个模块,接下来对这两个模块进行展开论述...

    TCCMS开源系统 v3.1

    内核借鉴了java中的struts和hibernate框架的一些思路,结合smarty MVC引擎自主研发的一套php ORM 框架。是一套高效,安全,开源的内容管理系统。 TCCMS 虽然功能不是最丰富的,可是是最实用的。最终目的是满足大...

    java web技术开发大全(最全最新)

    《Java Web开发技术大全:JSP+Servlet+Struts+Hibernate+Spring+Ajax》通过对SSH中的各种技术循序渐进地讲解,使读者尽快掌握开发基于SSH的Web程序的方法。《Java Web开发技术大全:JSP+Servlet+Struts+Hibernate+...

    mysql spring c3p0/dbcp/dbUtils工具支持包

    [Spring]基于Spring框架的Web应用演示(附带cglib工具进行动态代理) [Tomcat7.0]Tomcat7版本安装包 [UltraISO]制作U盘启动盘需要的 [log4j_jar]log4j的支持包 [myAutoLoginWeb]过滤器Filter学习-实现用户的自动登录与...

    Spring面试题

    2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作 3. hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。 4. hibernate的性能非常好,因为它是个...

    基于SSH的教材订购管理系统【项目源码+数据库脚本】(毕设)

    ​后台框架:Spring、Struts2、Hibernate ​数据库:MySQL 开发环境:JDK、Eclipse、Tomcat 三、系统功能 该系统的功能模块包括:教材征订模块、教材管理模块、教材领用模块、系统设置模块。 1.教材征订模块 教材...

    JSP基于SSH2网上书店购物网站-毕业设计-课程设计

    用了技术框架: HTML+CSS+JavaScript+jsp+mysql+Struts2+Spring+Hibernate --- 界面美观,功能齐全,适合用作毕业设计、课程设计作业等,项目均经过测试,可快速部署运行! 1、该资源内项目代码都经过测试运行成功...

    topJava:使用Java开发具有基于角色的授权和许可的功能齐全的Spring JPA Enterprise应用程序

    Java企业在线项目Java企业最需要的技术/工具/框架:Maven / Spring /安全性/ JPA(Hibernate)/ REST(杰克逊)/ Bootstrap(CSS)/ jQuery +插件。01.28:项目开始开始检查02.02交付HW0的截止日期04.02:第一堂课...

    最新最全的spring开发包

     这个jar文件包含Web应用开发时,用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类、Struts与JSF集成类、文件上传的支持类、Filter类和大量工具辅助类。 (12) spring-webmvc.jar 这个...

    Spring.3.x企业应用开发实战(完整版).part2

    17.4.5 使用Hibernate二级缓存 17.5 对持久层进行测试 17.5.1 配置Unitils测试环境 17.5.2 准备测试数据库及测试数据 17.5.3 编写DAO测试基类 17.5.4 编写BoardDao测试用例 17.6 服务层开发 17.6.1 UserService的...

    Spring3.x企业应用开发实战(完整版) part1

    17.4.5 使用Hibernate二级缓存 17.5 对持久层进行测试 17.5.1 配置Unitils测试环境 17.5.2 准备测试数据库及测试数据 17.5.3 编写DAO测试基类 17.5.4 编写BoardDao测试用例 17.6 服务层开发 17.6.1 UserService的...

Global site tag (gtag.js) - Google Analytics