Hibernate动态SQL查询

一、需求背景

给hibernate插上ibatis动态查询的翅膀,既保留crud的简洁性,又能收获ibatis的特性。

二、ibatis的动态查询

 1 <select id="findUser" resultClass="User">
 2     SELECT * From User
 3     <dynamic prepend="WHERE">
 4         <isNull property="id">
 5             id IS NULL
 6         </isNull>
 7         <isNotNull property="id">
 8             id = #id#
 9         </isNotNull>
10     </dynamic>
11 </select>

ibatis在程序内部解析sql语句中的标签,然后去解析计算

三、Freemarker 模版技术

利用freemarker把sql/hql中的动态拼接条件判断语法都交给freemarker语法去处理,既能复用freemarker框架,又保持了框架设计的简洁性

Freemarker 生成静态页面,首先需要使用自己定义的模板页面,这个模板页面可以是最最普通的html,也可以是嵌套freemarker中的 取值表达式, 标签或者自定义标签等等,然后后台读取这个模板页面,解析其中的标签完成相对应的操作, 然后采用键值对的方式传递参数替换模板中的的取值表达式,做完之后 根据配置的路径生成一个新的html页面, 以达到静态化访问的目的。

Freemarker标签都是<#标签名称>这样子命名的,${value} 表示输出变量名的内容

四、实例

User-dynamicHbSql.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!DOCTYPE dynamic-hibernate-statement PUBLIC "-//xxx/HOP Hibernate Dynamic Statement DTD 1.0//EN"
 3 "https://www.xxx.com/dtd/dynamic-hibernate-statement-1.0.dtd">
 4 <dynamic-hibernate-statement>
 5     <sql-query name="User.findUser">
 6     <![CDATA[
 7       select * from user where id=:id
 8     ]]>
 9     </sql-query>
10 </dynamic-hibernate-statement>

将sql语句编写的xml中,然后在java代码中,传入参数,经过freemarker处理,得到处理完的SQL. 好处:

  • sql编写在xml中,便于阅读
  • 可以使用freemarker语法,动态构建SQL
  • 可以使用freemarker的include语句,提取公用SQL

pom.xml

1 <!-- freemarker -->
2 <dependency>
3     <groupId>org.freemarker</groupId>
4     <artifactId>freemarker</artifactId>
5     <version>2.3.20</version>
6 </dependency>

web.xml

1 <servlet>
2     <servlet-name>sqlCache</servlet-name>
3     <servlet-class>xx.servlet.DynamicSqlInitServlet</servlet-class>
4     <init-param>
5         <param-name>fileName</param-name>
6         <param-value>classpath*:hibernate/**/*-dynamicHbSql.xml</param-value>
7     </init-param>
8     <load-on-startup>2</load-on-startup>
9 </servlet>

dtd定义:dynamic-hibernate-statement-1.0.dtd

 1 <!-- HOP Hibernate Dynamic Statement Mapping DTD.
 2 <!DOCTYPE dynamic-hibernate-statement PUBLIC
 3     "-//xxx/HOP Hibernate Dynamic Statement DTD 1.0//EN"
 4     "http://xxx.xxx.com/dtd/dynamic-hibernate-statement-1.0.dtd">
 5 这个文件时用来定义动态参数语句,类似itabis
 6 -->
 7
 8 <!--
 9     The document root.
10  -->
11
12 <!ELEMENT dynamic-hibernate-statement (
13     (hql-query|sql-query)*
14 )>
15 <!-- default: none -->
16
17 <!-- The query element declares a named Hibernate query string -->
18
19 <!ELEMENT hql-query (#PCDATA)>
20     <!ATTLIST hql-query name CDATA #REQUIRED>
21
22 <!-- The sql-query element declares a named SQL query string -->
23
24 <!ELEMENT sql-query (#PCDATA)>
25     <!ATTLIST sql-query name CDATA #REQUIRED>

DTD校验器/解析器

 1 package xxx.dao;
 2
 3 import java.io.InputStream;
 4 import java.io.Serializable;
 5
 6 import org.hibernate.internal.util.ConfigHelper;
 7 import org.slf4j.Logger;
 8 import org.slf4j.LoggerFactory;
 9 import org.xml.sax.EntityResolver;
10 import org.xml.sax.InputSource;
11
12 public class DynamicStatementDtdEntityResolver implements EntityResolver, Serializable {
13     private static final long serialVersionUID = 8123799007554762965L;
14     private static final Logger LOGGER = LoggerFactory.getLogger(DynamicStatementDtdEntityResolver.class);
15     private static final String HOP_DYNAMIC_STATEMENT = "https://xxx.xxx.com/dtd/";
16
17     @Override
18     public InputSource resolveEntity(String publicId, String systemId) {
19         InputSource source = null;
20         if (systemId != null) {
21             LOGGER.debug("trying to resolve system-id [" + systemId + "]");
22             if (systemId.startsWith(HOP_DYNAMIC_STATEMENT)) {
23                 LOGGER.debug(
24                         "recognized hop dyanmic statement namespace; attempting to resolve on classpath under xxx");
25                 source = resolveOnClassPath(publicId, systemId, HOP_DYNAMIC_STATEMENT);
26             }
27         }
28         return source;
29     }
30
31     private InputSource resolveOnClassPath(String publicId, String systemId, String namespace) {
32         InputSource source = null;
33         String path = "dtd/" + systemId.substring(namespace.length());
34         InputStream dtdStream = resolveInHibernateNamespace(path);
35         if (dtdStream == null) {
36             LOGGER.debug("unable to locate [" + systemId + "] on classpath");
37             String nameFlag = "2.0";
38             if (systemId.substring(namespace.length()).indexOf(nameFlag) > -1) {
39                 LOGGER.error("Don‘t use old DTDs, read the Hibernate 3.x Migration Guide!");
40             }
41         } else {
42             LOGGER.debug("located [" + systemId + "] in classpath");
43             source = new InputSource(dtdStream);
44             source.setPublicId(publicId);
45             source.setSystemId(systemId);
46         }
47         return source;
48     }
49
50     protected InputStream resolveInHibernateNamespace(String path) {
51         return this.getClass().getClassLoader().getResourceAsStream(path);
52     }
53
54     protected InputStream resolveInLocalNamespace(String path) {
55         try {
56             return ConfigHelper.getUserResourceAsStream(path);
57         } catch (Throwable t) {
58             return null;
59         }
60     }
61 }

加载*-dynamicHbSql.xml

  1 package xx.dao.servlet;
  2
  3 import java.io.IOException;
  4 import java.util.HashMap;
  5 import java.util.HashSet;
  6 import java.util.Iterator;
  7 import java.util.Map;
  8 import java.util.Set;
  9
 10 import javax.servlet.ServletContext;
 11 import javax.servlet.ServletException;
 12 import javax.servlet.http.HttpServlet;
 13
 14 import org.apache.commons.lang3.Validate;
 15 import org.dom4j.Document;
 16 import org.dom4j.Element;
 17 import org.hibernate.internal.util.xml.MappingReader;
 18 import org.hibernate.internal.util.xml.OriginImpl;
 19 import org.hibernate.internal.util.xml.XmlDocument;
 20 import org.slf4j.Logger;
 21 import org.slf4j.LoggerFactory;
 22 import org.springframework.core.io.Resource;
 23 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 24 import org.springframework.web.context.WebApplicationContext;
 25 import org.springframework.web.context.support.WebApplicationContextUtils;
 26 import org.xml.sax.EntityResolver;
 27 import org.xml.sax.InputSource;
 28
 29 import xx.dao.DynamicStatementDtdEntityResolver;
 30 import xx.cache.DynamicSqlInitCache;
 31
 32 /**
 33  * 持久层sql初始化加载类
 34  */
 35 public class DynamicSqlInitServlet extends HttpServlet {
 36
 37     private static final long serialVersionUID = 1L;
 38
 39     private static final Logger LOGGER = LoggerFactory.getLogger(DynamicSqlInitServlet.class);
 40
 41     private EntityResolver entityResolver = new DynamicStatementDtdEntityResolver();
 42
 43     private static DynamicSqlInitCache dynamicSqlInitCache;
 44
 45     /**
 46      *
 47      * 将sql初始化进入缓存中
 48      * @Title: init
 49      * @param @throws ServletException 设定文件
 50      * @author Administrator
 51      * @throws
 52      */
 53     @Override
 54     public void init() throws ServletException {
 55         super.init();
 56
 57         PathMatchingResourcePatternResolver resourceLoader = new PathMatchingResourcePatternResolver();
 58         Map<String, String> namedHQLQueries = new HashMap<String, String>(10);
 59         Map<String, String> namedSQLQueries = new HashMap<String, String>(10);
 60         Set<String> nameCache = new HashSet<String>();
 61         String fileName = this.getInitParameter("fileName");
 62         try {
 63             Resource[] resources = resourceLoader.getResources(fileName);
 64             buildMap(resources, namedHQLQueries, namedSQLQueries, nameCache);
 65
 66             ServletContext servletContext = this.getServletContext();
 67             WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
 68             dynamicSqlInitCache = (DynamicSqlInitCache) ctx.getBean("dynamicSqlInitCache");
 69
 70             for (String hqlKey : namedHQLQueries.keySet()) {
 71
 72                 dynamicSqlInitCache.getTemplateCacheByKey(hqlKey, namedHQLQueries.get(hqlKey), true);
 73
 74             }
 75
 76             for (String sqlKey : namedSQLQueries.keySet()) {
 77                 dynamicSqlInitCache.getTemplateCacheByKey(sqlKey, namedSQLQueries.get(sqlKey), false);
 78             }
 79         } catch (IOException e) {
 80             LOGGER.error("初始化sql缓存失败!", e);
 81         }
 82         // clear name cache
 83         namedHQLQueries.clear();
 84         namedSQLQueries.clear();
 85         nameCache.clear();
 86
 87     }
 88
 89     /**
 90      * 从文件中加载sql
 91      * @param resources
 92      * @param namedHQLQueries
 93      * @param namedSQLQueries
 94      * @param nameCache 设定文件
 95      * @throws ServletException
 96      */
 97     private void buildMap(Resource[] resources, Map<String, String> namedHQLQueries,
 98             Map<String, String> namedSQLQueries, Set<String> nameCache) throws ServletException {
 99         if (resources == null) {
100             return;
101         }
102         for (Resource resource : resources) {
103             InputSource inputSource = null;
104             try {
105                 inputSource = new InputSource(resource.getInputStream());
106                 XmlDocument metadataXml = MappingReader.INSTANCE.readMappingDocument(entityResolver, inputSource,
107                         new OriginImpl("file", resource.getFilename()));
108                 if (isDynamicStatementXml(metadataXml)) {
109                     final Document doc = metadataXml.getDocumentTree();
110                     final Element dynamicHibernateStatement = doc.getRootElement();
111                     Iterator rootChildren = dynamicHibernateStatement.elementIterator();
112                     while (rootChildren.hasNext()) {
113                         final Element element = (Element) rootChildren.next();
114                         final String elementName = element.getName();
115                         if ("sql-query".equals(elementName)) {
116                             putStatementToCacheMap(resource, element, namedSQLQueries, nameCache);
117                         } else if ("hql-query".equals(elementName)) {
118                             putStatementToCacheMap(resource, element, namedHQLQueries, nameCache);
119                         }
120                     }
121                 }
122             } catch (Exception e) {
123                 LOGGER.error(e.toString(),e);
124                 throw new ServletException(e.getMessage(), e);
125             } finally {
126                 if (inputSource != null && inputSource.getByteStream() != null) {
127                     try {
128                         inputSource.getByteStream().close();
129                     } catch (IOException e) {
130                         LOGGER.error(e.toString());
131                         throw new ServletException(e.getMessage(), e);
132                     }
133                 }
134             }
135
136         }
137
138     }
139
140     /**
141      * 将sql放入map中
142      * @param resource
143      * @param element
144      * @param statementMap
145      * @param nameCache
146      * @throws IOException 设定文件
147      */
148     private void putStatementToCacheMap(Resource resource, final Element element, Map<String, String> statementMap,
149             Set<String> nameCache) throws Exception {
150         String sqlQueryName = element.attribute("name").getText();
151         Validate.notEmpty(sqlQueryName);
152         if (nameCache.contains(sqlQueryName)) {
153             throw new Exception("重复的sql-query/hql-query语句定义在文件:" + resource.getURI() + "中,必须保证name的唯一.");
154         }
155         nameCache.add(sqlQueryName);
156         String queryText = element.getText();
157         statementMap.put(sqlQueryName, queryText);
158     }
159
160     /**
161      * 判断是否是贮存sql的xml文件
162      * @param xmlDocument
163      * @return boolean 返回类型
164      */
165     private static boolean isDynamicStatementXml(XmlDocument xmlDocument) {
166         return "dynamic-hibernate-statement".equals(xmlDocument.getDocumentTree().getRootElement().getName());
167     }
168
169 }
 1 package xxx.cache;
 2
 3 import java.io.IOException;
 4 import java.io.StringReader;
 5
 6 import org.springframework.cache.annotation.Cacheable;
 7 import org.springframework.stereotype.Component;
 8
 9 import xxx.dao.StatementTemplate;
10
11 import freemarker.cache.StringTemplateLoader;
12 import freemarker.template.Configuration;
13 import freemarker.template.Template;
14
15 /**
16  * Sql缓存类
17  */
18 @Component
19 public class DynamicSqlInitCache {
20
21     private Configuration configuration;
22
23     private StringTemplateLoader stringLoader;
24
25     /**
26      * 根据key获取sql对应的StatementTemplate
27      * @param key
28      * @param sql
29      * @param isHql
30      * @return StatementTemplate
31      * @throws IOException
32      */
33     @Cacheable(value = "sqlCache", key = "#key")
34     public StatementTemplate getTemplateCacheByKey(String key, String sql, boolean isHql) throws IOException {
35         if (configuration == null) {
36             configuration =new Configuration();
37         }
38         if (stringLoader == null) {
39             stringLoader =new StringTemplateLoader();
40         }
41         configuration.setNumberFormat("#");
42         if (isHql) {
43             stringLoader.putTemplate(key, sql);
44             StatementTemplate statementTemplate = new StatementTemplate(StatementTemplate.TYPE.HQL,new Template(key,new StringReader(sql),configuration));
45             configuration.setTemplateLoader(stringLoader);
46             return statementTemplate;
47         } else {
48             stringLoader.putTemplate(key, sql);
49             StatementTemplate statementTemplat e= new StatementTemplate(StatementTemplate.TYPE.SQL,new Template(key,new StringReader(sql),configuration));
50             configuration.setTemplateLoader(stringLoader);
51             return statementTemplate;
52         }
53     }
54 }
 1 package xxx.dao;
 2
 3 import java.io.Serializable;
 4
 5 import freemarker.template.Template;
 6
 7 /**
 8  * @Description: sql模板类
 9  */
10 public class StatementTemplate implements Serializable{
11     /**
12      *
13      */
14     private static final long serialVersionUID = 6887405509065040282L;
15
16     private Template template;
17
18     private TYPE type;
19
20     public StatementTemplate(TYPE type, Template template) {
21         this.template = template;
22         this.type = type;
23     }
24
25     public TYPE getType() {
26         return type;
27     }
28
29     public void setType(TYPE type) {
30         this.type = type;
31     }
32
33     public Template getTemplate() {
34         return template;
35     }
36
37     public void setTemplate(Template template) {
38         this.template = template;
39     }
40
41     public static enum TYPE {
42         /**
43          * HQL
44          */
45         HQL,
46         SQL
47     }
48 }

原文地址:https://www.cnblogs.com/angelica-duhurica/p/9766392.html

时间: 2024-10-10 16:06:57

Hibernate动态SQL查询的相关文章

kettle中实现动态SQL查询

kettle中实现动态SQL查询 在ETL项目中,通常有根据运行时输入参数去执行一些SQL语句,如查询数据.本文通过kettle中的表输入("table input")步骤来说明动态查询.参数查询.示例代码使用内存数据库(H2),下载就可以直接运行,通过示例学习更轻松. SQL查询语句中占位符绑定字段值 第一个接近动态语句的是大家熟悉的从SQL代码中执行,开始写一个SQL查询,包含一些占位符,然后绑定值到占位符,使之成为一个有效的查询并执行.根据需要可以绑定多个值并循环执行.本示例的名

使用hibernate原生sql查询,结果集全为1的问题解决

问题如下: String sqlTest ="select summary,summaryno from F_Summary"; List<Map<Object, Object>> listTest = this.getService().getListBySql(sqlTest); for (Map<Object, Object> m : listTest) { for (Object k : m.keySet()) { System.out.pr

Hibernate应用SQL查询返回实体类型

Hibernate应用SQL查询返回实体类型 Hibernate使用SQL查询返回实体类型 以前,使用SQL查询,结果放在 RS 结果集中,还要去转换影射到Java类中.Hibernate中,可以自动帮我们注入到Hibernate 甚至是 非 Hibernate 类型当中. String sql = "select * from T_TMP_PUBLIC_POLICY TP"; SQLQuery sqluery = session.createSQLQuery(sql).addEnti

关于用Hibernate执行sql查询,字符字段类型只返回第一个字节的问题

关于用Hibernate执行sql查询,字符字段类型只返回第一个字节的问题 今天遇到了一个问题,就是在Hibernate中,我用sql去查询mysql数据库里面的user表里面的username字段,但是发现查出来的数据都是只有第一个字,例如:在user表里面的username字段中有一条数据是:"Chen chiwei",查询后返回的结果却是这条数据的第一个字母:"C":于是乎,我查了一下user这张表的设计,发现username这个字段的类型是'char'类型,

Hibernate原生SQL查询多表关联,SQL语句要注意的问题

Hibernate原生SQL查询多表关联,SQL语句要注意的问题 @for&ever 2009-9-4 系统环境: MySQL5.1 Hibernate3.3 有如下的假定: 实体类 Question 和 Answer分别对应数据表 question 和answer. 并且表 question 和answer 的字段大部分都一样,字段数目也一样. 执行如下的操作: 1> 使用hibernate 使用原生SQL查询, Query q = session.createSQLQuery(sql).

Hibernate之SQL查询

Hibernate支持使用原生的SQL语句进行查询.使用原生的SQL的好处是:可以利用某些数据库的特性(不同的数据库SQL 语法会有所差异), 将原有的使用JDBC作为持久层技术的应用 ,迁移到使用Hibernate作为持久层技术. 我们知道 使用HQL查询 主要牵涉的是 Query接口,而使用SQL查询 主要牵涉的 是 SQLQuery接口  . 而SQLQuery接口 是 Query接口的子接口. 所以 Query接口中的方法 ,均可以在SQLQuery接口中使用 ,SQLQuery接口还多

一次动态sql查询订单数据的设计

订单数据一般是变化比较多的数据,查询条件也是比较复杂,如果没有一个规则解释,写接口的变动就会比较频繁:由于个人不喜欢在业务端使用拼接sql的方式,另外本身是做安全出身,业务端的sql拼接容易sql注入,所以不考虑使用:为了满足这个特性,将订单的条件进行了一次划分: 状态.用户.时间.公司.上车地点.下车地点: 第一步: 先处理状态查询, 状态查询定义在t_om_group表中,后台人员可以手动添加状态分组,前端业务只需要状态分组对应哪些状态即可:如: group_id 1 对应 order_st

MyBatis的动态SQL查询-让查询更灵活多变!

序言 MyBatis,大家都知道,半自动的ORM框架,原来叫ibatis,后来好像是10年apache软件基金组织把它托管给了goole code,就重新命名了MyBatis,功能相对以前更强大了.它相对全自动的持久层框架Hibernate,更加灵活,更轻量级,这点我还是深有体会的. MyBatis的一个强大特性之一就是动态SQL能力了,能省去我们很多串联判断拼接SQL的痛苦,根据项目而定,在一定的场合下使用,能大大减少程序的代码量和复杂程度,不过还是不是过度太过复杂的使用,以免不利于后期的维护

获取动态SQL查询语句返回值(sp_executesql)

在写存储过程时经常会遇到需要拼接SQL语句的情况,一般情况下仅仅是为了执行拼接后的语句使用exec(@sql)即可. 而今天的一个存储过程却需要获取动态SQL的查询结果. 需求描述:在某表中根据Id值查询Cost值(表名不确定但表结构确定,如下面的Product表) 如果不考虑获取返回值,我们这样写即可: declare @tableName varchar(50) declare @id varchar(10) declare @cost numeric(18,2) declare @sql