hibernate根据条件动态组装sql/hql语句(仿ibatis动态查询语句功

本文介绍了一种使用Freemarker模板技术动态组装Hibernate SQL/HQL语句的方法,以实现类似iBatis的动态查询功能。在系统加载阶段,动态SQL文件被加载并缓存;在查询阶段,通过传入的参数解析模板,生成最终的SQL/HQL,再由底层API执行查询。整个流程包括DTD定义、SQL文件编写和代码实现。
摘要由CSDN通过智能技术生成
1.功能需求背景
项目中使用hibernate作为数据持久层框架,主要考虑hibernate在进行一些简单的crud操作时非常便利,不需要和ibatis似的为每个sql操作都写一堆文件,但是同时也带来了一些局限性,如类似ibatis强大的动态查询功能用不了了,但是这个功能在项目中的应用场景又很大,hibernate自身也支持将sql/hql语句写在.hbm.xml映射文件中<sql-query>和<query>元素,但是这个功能只能对那些查询限制条件固定的sql有用,对于需要动态拼接的sql语句,hibernate就显得力不从心了,如何给hibernate插上ibatis动态查询的翅膀,既保留crud的简洁性,又能收获ibatis的特性呢?接下来的文章将会重点介绍
2.设计思路
先看一下ibatis的动态查询时怎么做的
<select id="getUserList" re sultMap="user">
select * from user
<isGreaterThan prepend="and" property="id" compareValue="0">
where user_id = #userId#
</isGreaterThan>
order by createTime desc
</select>
ibatis在程序实现内部回去解析sql语句中的标签,然后去解析计算,我们在ibatis在实现的时候也参考了这个解决思路,但是否是需要把ibatis里的解析sql的语法都抄到我们的dao框架中呢-显然这样太复杂了,而且ibatis自己的sql元素是和那些resultMap等是绑定在一起用的,而在hibernate是没用这些东西的,要改造这些东西是一项非常浩大的工程,因此这个方案被放弃了

我们在实现的时候采取了一种非常简洁又功能强大的方式-模板技术!对,就是利用freemarker把sql/hql中的动态拼接条件判断语法都交给freemarker语法去处理,这样既能复用freemarker框架,又保持了我们框架设计的简洁性-不需要自己写过多的处理逻辑,以下是我们需要进行动态处理的sql/hql语句的样例

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dynamic-hibernate-statement PUBLIC "-//Haier/HOP Hibernate Dynamic Statement DTD 1.0//EN"
"http://www.haier.com/dtd/dynamic-hibernate-statement-1.0.dtd">
<dynamic-hibernate-statement>
	<!-- 查询某个资源下的直接子节点 -->
	<hql-query name="resource.getChildren">
	<![CDATA[
		from Resource where parent.id=${parentId} and parent.id != id
	]]> 
	</hql-query>
	<!-- 查询系统中所有的root资源 -->
	<hql-query name="resource.getRoots">
	<![CDATA[
		from Resource where parent.id = id order by orderIndex
	]]> 
	</hql-query>
	<!-- 获取某个用户可访问的某个资源下的所有子资源 -->
	<sql-query name="resource.getDescendants">
	<![CDATA[
		select distinct t.id,
		                t.name,
		                t.description,
		                t.url,
		                t.type,
		                t.status,
		                t.code,
		                t.configuration,
		                t.module_name,
		                t.gmt_create,
		                t.gmt_modified,
		                t.create_by,
		                t.last_modified_by,
		                t.order_index,
		                t.parent_id
		  from resource_info t
		 inner join role_resource rr
		    on t.id = rr.resource_id
		 inner join user_role ur
		    on rr.role_id = ur.role_id
		 where ur.user_id = ${userId}
		 <#if type == '1'>
		 	and t.type=1
		 	<#else>
		 	and t.type=0
		 </#if>
		   and t.type =  ${type}
		   and t.status =  ${status}
		 start with t.code = '${code}'
		connect by nocycle prior t.id = t.parent_id
	]]> 
	</sql-query>
</dynamic-hibernate-statement>
这个文件看起来非常类似ibatis的语句了,只是没用ibatis的哪些标签-改成了freemarker语法,没错,我们就是复用freemarker来帮我们解决这些烦杂的判断操作的

这样我们的动态sql程序就可以总结成以下流程

a.系统加载阶段

这个阶段程序负责将指定路径下的动态sql文件加载到内存中,一次性缓存起来,没错,这些东西只需要加载一次,以后直接读取就行了,没必要每次去查找,缓存也非常简单,一个Map<String,String>就搞定,key是sql-query或hql-query元素的name属性,value就是与其对应的sql/hql语句

b.程序调用查询阶段

调用程序通过sql/hql语句的name属性和传入查询参数来得到最终解析出来的语句

我们期望的方法可能是这样的:

public <X> List<X> findByNamedQuery(final String queryName, final Map<String, ?> parameters) 
通过queryName从缓存中查找出其对应的sql/hql语句(最原始的,里面带有freemarker语法)

然后通过freemarker模板和传递进去的parameters参数对模板进行解析,得到最终的语句(纯sql/hql)

最后将解析后的sql/hql传递给底层api,返回查询结果

3.实现

上面介绍了大致的思路,这里介绍具体的代码实现

3.1DTD定义

我们是把动态的sql/hql语句放在单独的xml配置文件里的,为了规范xml文档,我们给文档定义了dtd文件,这里我们只定义了两个元素<sql-query>和<hql-query>分别表示sql查询语句和hql查询语句,这两个元素目前自有一个name属性用来唯一标示该语句,如下

<!-- HOP Hibernate Dynamic Statement Mapping DTD.

<!DOCTYPE dynamic-hibernate-statement PUBLIC 
    "-//Haier/HOP Hibernate Dynamic Statement DTD 1.0//EN"
    "http://www.haier.com/dtd/dynamic-hibernate-statement-1.0.dtd">

这个文件时用来定义动态参数语句,类似itabis

-->

<!--
	The document root.
 -->

<!ELEMENT dynamic-hibernate-statement (
	(hql-query|sql-query)*
)>
							<!-- default: none -->

<!-- The query element declares a named Hibernate query string -->

<!ELEMENT hql-query (#PCDATA)>
	<!ATTLIST hql-query name CDATA #REQUIRED>

<!-- The sql-query element declares a named SQL query string -->

<!ELEMENT sql-query (#PCDATA)>
	<!ATTLIST sql-query name CDATA #REQUIRED>

然后将其保存为dynamic-hibernate-statement-1.0.dtd,放在classpath下

编写DTD校验器

/**
 * hibernate动态sql dtd解析器
 * @author WangXuzheng
 *
 */
public class DynamicStatementDTDEntityResolver implements EntityResolver, Serializable{
	private static final long serialVersionUID = 8123799007554762965L;
	private static final Logger LOGGER = LoggerFactory.getLogger( DynamicStatementDTDEntityResolver.class );
	private s
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值