Hibernate_根据条件动态组建sql/hql语句(仿ibatis动态查询语句功能)

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

via: http://blog.csdn.net/crazycoder2010/article/details/7414152

 

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" resultMap="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语句的样例

[html] view plaincopyprint?
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!DOCTYPE dynamic-hibernate-statement PUBLIC "-//Haier/HOP Hibernate Dynamic Statement DTD 1.0//EN"  
  3. "http://www.haier.com/dtd/dynamic-hibernate-statement-1.0.dtd">  
  4. <dynamic-hibernate-statement>  
  5.     <!-- 查询某个资源下的直接子节点 -->  
  6.     <hql-query name="resource.getChildren">  
  7.     <![CDATA[ 
  8.         from Resource where parent.id=${parentId} and parent.id != id 
  9.     ]]>   
  10.     </hql-query>  
  11.     <!-- 查询系统中所有的root资源 -->  
  12.     <hql-query name="resource.getRoots">  
  13.     <![CDATA[ 
  14.         from Resource where parent.id = id order by orderIndex 
  15.     ]]>   
  16.     </hql-query>  
  17.     <!-- 获取某个用户可访问的某个资源下的所有子资源 -->  
  18.     <sql-query name="resource.getDescendants">  
  19.     <![CDATA[ 
  20.         select distinct t.id, 
  21.                         t.name, 
  22.                         t.description, 
  23.                         t.url, 
  24.                         t.type, 
  25.                         t.status, 
  26.                         t.code, 
  27.                         t.configuration, 
  28.                         t.module_name, 
  29.                         t.gmt_create, 
  30.                         t.gmt_modified, 
  31.                         t.create_by, 
  32.                         t.last_modified_by, 
  33.                         t.order_index, 
  34.                         t.parent_id 
  35.           from resource_info t 
  36.          inner join role_resource rr 
  37.             on t.id = rr.resource_id 
  38.          inner join user_role ur 
  39.             on rr.role_id = ur.role_id 
  40.          where ur.user_id = ${userId} 
  41.          <#if type == '1'> 
  42.             and t.type=1 
  43.             <#else> 
  44.             and t.type=0 
  45.          </#if> 
  46.            and t.type =  ${type} 
  47.            and t.status =  ${status} 
  48.          start with t.code = '${code}' 
  49.         connect by nocycle prior t.id = t.parent_id 
  50.     ]]>   
  51.     </sql-query>  
  52. </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属性和传入查询参数来得到最终解析出来的语句

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

[java] view plaincopyprint?
 
  1. 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属性用来唯一标示该语句,如下

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

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

编写DTD校验器

 

[java] view plaincopyprint?
 
  1. /** 
  2.  * hibernate动态sql dtd解析器 
  3.  * @author WangXuzheng 
  4.  * 
  5.  */  
  6. public class DynamicStatementDTDEntityResolver implements EntityResolver, Serializable{  
  7.     private static final long serialVersionUID = 8123799007554762965L;  
  8.     private static final Logger LOGGER = LoggerFactory.getLogger( DynamicStatementDTDEntityResolver.class );  
  9.     private static final String HOP_DYNAMIC_STATEMENT = "http://www.haier.com/dtd/";  
  10.   
  11.     public InputSource resolveEntity(String publicId, String systemId) {  
  12.         InputSource source = null// returning null triggers default behavior  
  13.         if ( systemId != null ) {  
  14.             LOGGER.debug( "trying to resolve system-id [" + systemId + "]" );  
  15.             if ( systemId.startsWith( HOP_DYNAMIC_STATEMENT ) ) {  
  16.                 LOGGER.debug( "recognized hop dyanmic statement namespace; attempting to resolve on classpath under com/haier/openplatform/dao/hibernate/" );  
  17.                 source = resolveOnClassPath( publicId, systemId, HOP_DYNAMIC_STATEMENT );  
  18.             }  
  19.         }  
  20.         return source;  
  21.     }  
  22.   
  23.     private InputSource resolveOnClassPath(String publicId, String systemId, String namespace) {  
  24.         InputSource source = null;  
  25.         String path = "com/haier/openplatform/dao/hibernate/" + systemId.substring( namespace.length() );  
  26.         InputStream dtdStream = resolveInHibernateNamespace( path );  
  27.         if ( dtdStream == null ) {  
  28.             LOGGER.debug( "unable to locate [" + systemId + "] on classpath" );  
  29.             if ( systemId.substring( namespace.length() ).indexOf( "2.0" ) > -1 ) {  
  30.                 LOGGER.error( "Don't use old DTDs, read the Hibernate 3.x Migration Guide!" );  
  31.             }  
  32.         }  
  33.         else {  
  34.             LOGGER.debug( "located [" + systemId + "] in classpath" );  
  35.             source = new InputSource( dtdStream );  
  36.             source.setPublicId( publicId );  
  37.             source.setSystemId( systemId );  
  38.         }  
  39.         return source;  
  40.     }  
  41.   
  42.     protected InputStream resolveInHibernateNamespace(String path) {  
  43.         return this.getClass().getClassLoader().getResourceAsStream( path );  
  44.     }  
  45.   
  46.     protected InputStream resolveInLocalNamespace(String path) {  
  47.         try {  
  48.             return ConfigHelper.getUserResourceAsStream( path );  
  49.         }  
  50.         catch ( Throwable t ) {  
  51.             return null;  
  52.         }  
  53.     }  
  54. }  

 

3.2编写sql文件

[html] view plaincopyprint?
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!DOCTYPE dynamic-hibernate-statement PUBLIC "-//Haier/HOP Hibernate Dynamic Statement DTD 1.0//EN"  
  3. "http://www.haier.com/dtd/dynamic-hibernate-statement-1.0.dtd">  
  4. <dynamic-hibernate-statement>  
  5.     <!-- 查询某个资源下的直接子节点 -->  
  6.     <hql-query name="resource.getChildren">  
  7.     <![CDATA[ 
  8.         from Resource where parent.id=${parentId} and parent.id != id 
  9.     ]]>   
  10.     </hql-query>  
  11.     <!-- 查询系统中所有的root资源 -->  
  12.     <hql-query name="resource.getRoots">  
  13.     <![CDATA[ 
  14.         from Resource where parent.id = id order by orderIndex 
  15.     ]]>   
  16.     </hql-query>  
  17.     <!-- 获取某个用户可访问的某个资源下的所有子资源 -->  
  18.     <sql-query name="resource.getDescendants">  
  19.     <![CDATA[ 
  20.         select distinct t.id, 
  21.                         t.name, 
  22.                         t.description, 
  23.                         t.url, 
  24.                         t.type, 
  25.                         t.status, 
  26.                         t.code, 
  27.                         t.configuration, 
  28.                         t.module_name, 
  29.                         t.gmt_create, 
  30.                         t.gmt_modified, 
  31.                         t.create_by, 
  32.                         t.last_modified_by, 
  33.                         t.order_index, 
  34.                         t.parent_id 
  35.           from resource_info t 
  36.          inner join role_resource rr 
  37.             on t.id = rr.resource_id 
  38.          inner join user_role ur 
  39.             on rr.role_id = ur.role_id 
  40.          where ur.user_id = ${userId} 
  41.          <#if type == '1'> 
  42.             and t.type=1 
  43.             <#else> 
  44.             and t.type=0 
  45.          </#if> 
  46.            and t.type =  ${type} 
  47.            and t.status =  ${status} 
  48.          start with t.code = '${code}' 
  49.         connect by nocycle prior t.id = t.parent_id 
  50.     ]]>   
  51.     </sql-query>  
  52. </dynamic-hibernate-statement>  

3.3加载动态sql文件

这里我们将加载sql/hql语句的程序独立到一个单独的类中,以便独立扩展

这里一共3个方法,分表标示获取系统中sql/hql语句的map(key:语句名称,value:具体的)

[java] view plaincopyprint?
 
  1. /** 
  2.  * 动态sql/hql语句组装器 
  3.  * @author WangXuzheng 
  4.  * 
  5.  */  
  6. public interface DynamicHibernateStatementBuilder {  
  7.     /** 
  8.      * hql语句map 
  9.      * @return 
  10.      */  
  11.     public Map<String,String> getNamedHQLQueries();  
  12.     /** 
  13.      * sql语句map 
  14.      * @return 
  15.      */  
  16.     public Map<String,String> getNamedSQLQueries();  
  17.     /** 
  18.      * 初始化 
  19.      * @throws IOException  
  20.      */  
  21.     public void init() throws IOException;  
  22. }  

默认的加载器-将指定配置文件中的sql/hql语句加载到内存中

[java] view plaincopyprint?
 
  1. /** 
  2.  * @author WangXuzheng 
  3.  * 
  4.  */  
  5. public class DefaultDynamicHibernateStatementBuilder implements DynamicHibernateStatementBuilder, ResourceLoaderAware {  
  6.     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDynamicHibernateStatementBuilder.class);  
  7.     private Map<String, String> namedHQLQueries;  
  8.     private Map<String, String> namedSQLQueries;  
  9.     private String[] fileNames = new String[0];  
  10.     private ResourceLoader resourceLoader;  
  11.     private EntityResolver entityResolver = new DynamicStatementDTDEntityResolver();  
  12.     /** 
  13.      * 查询语句名称缓存,不允许重复 
  14.      */  
  15.     private Set<String> nameCache = new HashSet<String>();  
  16.   
  17.     public void setFileNames(String[] fileNames) {  
  18.         this.fileNames = fileNames;  
  19.     }  
  20.   
  21.     @Override  
  22.     public Map<String, String> getNamedHQLQueries() {  
  23.         return namedHQLQueries;  
  24.     }  
  25.   
  26.     @Override  
  27.     public Map<String, String> getNamedSQLQueries() {  
  28.         return namedSQLQueries;  
  29.     }  
  30.   
  31.     @Override  
  32.     public void init() throws IOException {  
  33.         namedHQLQueries = new HashMap<String, String>();  
  34.         namedSQLQueries = new HashMap<String, String>();  
  35.         boolean flag = this.resourceLoader instanceof ResourcePatternResolver;  
  36.         for (String file : fileNames) {  
  37.             if (flag) {  
  38.                 Resource[] resources = ((ResourcePatternResolver) this.resourceLoader).getResources(file);  
  39.                 buildMap(resources);  
  40.             } else {  
  41.                 Resource resource = resourceLoader.getResource(file);  
  42.                 buildMap(resource);  
  43.             }  
  44.         }  
  45.         //clear name cache  
  46.         nameCache.clear();  
  47.     }  
  48.   
  49.     @Override  
  50.     public void setResourceLoader(ResourceLoader resourceLoader) {  
  51.         this.resourceLoader = resourceLoader;  
  52.     }  
  53.   
  54.     private void buildMap(Resource[] resources) throws IOException {  
  55.         if (resources == null) {  
  56.             return;  
  57.         }  
  58.         for (Resource resource : resources) {  
  59.             buildMap(resource);  
  60.         }  
  61.     }  
  62.   
  63.     @SuppressWarnings({ "rawtypes" })  
  64.     private void buildMap(Resource resource) {  
  65.         InputSource inputSource = null;  
  66.         try {  
  67.             inputSource = new InputSource(resource.getInputStream());  
  68.             XmlDocument metadataXml = MappingReader.INSTANCE.readMappingDocument(entityResolver, inputSource,  
  69.                     new OriginImpl("file", resource.getFilename()));  
  70.             if (isDynamicStatementXml(metadataXml)) {  
  71.                 final Document doc = metadataXml.getDocumentTree();  
  72.                 final Element dynamicHibernateStatement = doc.getRootElement();  
  73.                 Iterator rootChildren = dynamicHibernateStatement.elementIterator();  
  74.                 while (rootChildren.hasNext()) {  
  75.                     final Element element = (Element) rootChildren.next();  
  76.                     final String elementName = element.getName();  
  77.                     if ("sql-query".equals(elementName)) {  
  78.                         putStatementToCacheMap(resource, element, namedSQLQueries);  
  79.                     } else if ("hql-query".equals(elementName)) {  
  80.                         putStatementToCacheMap(resource, element, namedHQLQueries);  
  81.                     }  
  82.                 }  
  83.             }  
  84.         } catch (Exception e) {  
  85.             LOGGER.error(e.toString());  
  86.             throw new SysException(e);  
  87.         } finally {  
  88.             if (inputSource != null && inputSource.getByteStream() != null) {  
  89.                 try {  
  90.                     inputSource.getByteStream().close();  
  91.                 } catch (IOException e) {  
  92.                     LOGGER.error(e.toString());  
  93.                     throw new SysException(e);  
  94.                 }  
  95.             }  
  96.         }  
  97.   
  98.     }  
  99.   
  100.     private void putStatementToCacheMap(Resource resource, final Element element, Map<String, String> statementMap)  
  101.             throws IOException {  
  102.         String sqlQueryName = element.attribute("name").getText();  
  103.         Validate.notEmpty(sqlQueryName);  
  104.         if (nameCache.contains(sqlQueryName)) {  
  105.             throw new SysException("重复的sql-query/hql-query语句定义在文件:" + resource.getURI() + "中,必须保证name的唯一.");  
  106.         }  
  107.         nameCache.add(sqlQueryName);  
  108.         String queryText = element.getText();  
  109.         statementMap.put(sqlQueryName, queryText);  
  110.     }  
  111.   
  112.     private static boolean isDynamicStatementXml(XmlDocument xmlDocument) {  
  113.         return "dynamic-hibernate-statement".equals(xmlDocument.getDocumentTree().getRootElement().getName());  
  114.     }  
  115. }  

配置一下

[html] view plaincopyprint?
 
  1.     <bean id="dynamicStatementBuilder" class="com.haier.openplatform.dao.hibernate.support.DefaultDynamicHibernateStatementBuilder">  
  2.         <property name="fileNames">  
  3.             <list>  
  4.                 <value>classpath*:/**/*-dynamic.xml</value><!--这里我们指定要加载某个文件夹下所有以-dynamic.xml结尾的文件 -->  
  5.             </list>  
  6.         </property>  
  7.     </bean>  
  8.     <bean id="baseHibernateDAO" class="com.haier.openplatform.dao.hibernate.BaseDAOHibernateImpl" abstract="true">  
  9.         <property name="sessionFactory">  
  10.             <ref bean="sessionFactory"/>  
  11.         </property>  
  12.         <property name="dynamicStatementBuilder" ref="dynamicStatementBuilder"/>  
  13.     </bean>  

dao层代码

[java] view plaincopyprint?
 
  1. /** 
  2.  * Hibernate实现的DAO层 
  3.  * @param <T> DAO操作的对象类型 
  4.  * @param <ID> 主键类型 
  5.  * @author WangXuzheng 
  6.  * 
  7.  */  
  8. public class SimpleHibernateDAO<T,ID extends Serializable> implements BaseDAO<T, ID>,InitializingBean{  
  9.     private static final Logger LOGER = LoggerFactory.getLogger(SimpleHibernateDAO.class);  
  10.     protected SessionFactory sessionFactory;  
  11.     protected Class<T> entityClass;  
  12.     /** 
  13.      * 模板缓存 
  14.      */  
  15.     protected Map<String, StatementTemplate> templateCache;  
  16.     protected DynamicHibernateStatementBuilder dynamicStatementBuilder;  
  17.     /** 
  18.      * 通过子类的泛型定义取得对象类型Class. 
  19.      * eg. 
  20.      * public class UserDao extends SimpleHibernateDao<User, Long> 
  21.      */  
  22.     public SimpleHibernateDAO() {  
  23.         this.entityClass = Reflections.getSuperClassGenricType(getClass());  
  24.     }  
  25.   
  26.     /** 
  27.      * 取得sessionFactory. 
  28.      */  
  29.     public SessionFactory getSessionFactory() {  
  30.         return sessionFactory;  
  31.     }  
  32.   
  33.     /** 
  34.      * 采用@Autowired按类型注入SessionFactory, 当有多个SesionFactory的时候在子类重载本函数. 
  35.      */  
  36.     public void setSessionFactory(final SessionFactory sessionFactory) {  
  37.         this.sessionFactory = sessionFactory;  
  38.     }  
  39.   
  40.     public void setDynamicStatementBuilder(DynamicHibernateStatementBuilder dynamicStatementBuilder) {  
  41.         this.dynamicStatementBuilder = dynamicStatementBuilder;  
  42.     }  
  43.   
  44.     /** 
  45.      * 取得当前Session. 
  46.      */  
  47.     public Session getSession() {  
  48.         return sessionFactory.getCurrentSession();  
  49.     }  
  50.   
  51.     /** 
  52.      * 保存新增或修改的对象. 
  53.      */  
  54.     @Override  
  55.     public void save(final T entity) {  
  56.         Validate.notNull(entity, "entity不能为空");  
  57.         getSession().save(entity);  
  58.         LOGER.debug("save entity: {}", entity);  
  59.     }  
  60.   
  61.     /** 
  62.      * 删除对象. 
  63.      *  
  64.      * @param entity 对象必须是session中的对象或含id属性的transient对象. 
  65.      */  
  66.     @Override  
  67.     public void delete(final T entity) {  
  68.         if(entity == null){  
  69.             return;  
  70.         }  
  71.         getSession().delete(entity);  
  72.         LOGER.debug("delete entity: {}", entity);  
  73.     }  
  74.   
  75.     /** 
  76.      * 按id删除对象. 
  77.      */  
  78.     @Override  
  79.     public void delete(final ID id) {  
  80.         Validate.notNull(id, "id不能为空");  
  81.         delete(get(id));  
  82.         LOGER.debug("delete entity {},id is {}", entityClass.getSimpleName(), id);  
  83.     }  
  84.   
  85.     /** 
  86.      * 按id获取对象. 
  87.      */  
  88.     @SuppressWarnings("unchecked")  
  89.     @Override  
  90.     public T get(final ID id) {  
  91.         Validate.notNull(id, "id不能为空");  
  92.         return (T) getSession().get(entityClass, id);  
  93.     }  
  94.   
  95.     /** 
  96.      * 按id列表获取对象列表. 
  97.      */  
  98.     public List<T> get(final Collection<ID> ids) {  
  99.         return find(Restrictions.in(getIdName(), ids));  
  100.     }  
  101.   
  102.     /** 
  103.      *  获取全部对象. 
  104.      */  
  105.     @Override  
  106.     public List<T> getAll() {  
  107.         return find();  
  108.     }  
  109.   
  110.     /** 
  111.      *  获取全部对象, 支持按属性行序. 
  112.      */  
  113.     @SuppressWarnings("unchecked")  
  114.     public List<T> getAll(String orderByProperty, boolean isAsc) {  
  115.         Criteria c = createCriteria();  
  116.         if (isAsc) {  
  117.             c.addOrder(Order.asc(orderByProperty));  
  118.         } else {  
  119.             c.addOrder(Order.desc(orderByProperty));  
  120.         }  
  121.         return c.list();  
  122.     }  
  123.   
  124.     /** 
  125.      * 按属性查找对象列表, 匹配方式为相等. 
  126.      */  
  127.     public List<T> findBy(final String propertyName, final Object value) {  
  128.         Criterion criterion = Restrictions.eq(propertyName, value);  
  129.         return find(criterion);  
  130.     }  
  131.   
  132.     /** 
  133.      * 按属性查找唯一对象, 匹配方式为相等. 
  134.      */  
  135.     @SuppressWarnings("unchecked")  
  136.     @Override  
  137.     public T findUniqueBy(final String propertyName, final Object value) {  
  138.         Criterion criterion = Restrictions.eq(propertyName, value);  
  139.         return ((T) createCriteria(criterion).uniqueResult());  
  140.     }  
  141.   
  142.     /** 
  143.      * 按HQL查询对象列表. 
  144.      *  
  145.      * @param values 数量可变的参数,按顺序绑定. 
  146.      */  
  147.     @SuppressWarnings("unchecked")  
  148.     public <X> List<X> findByHQL(final String hql, final Object... values) {  
  149.         return createHQLQuery(hql, values).list();  
  150.     }  
  151.       
  152.     /** 
  153.      * 按HQL查询对象列表,并将对象封装成指定的对象 
  154.      *  
  155.      * @param values 数量可变的参数,按顺序绑定. 
  156.      */  
  157.     @SuppressWarnings("unchecked")  
  158.     public <X> List<X> findByHQLRowMapper(RowMapper<X> rowMapper,final String hql, final Object... values) {  
  159.         Validate.notNull(rowMapper, "rowMapper不能为空!");  
  160.         List<Object[]> result = createHQLQuery(hql, values).list();  
  161.         return buildListResultFromRowMapper(rowMapper, result);  
  162.     }  
  163.   
  164.     protected <X> List<X> buildListResultFromRowMapper(RowMapper<X> rowMapper, List<Object[]> result) {  
  165.         List<X> rs = new ArrayList<X>(result.size());  
  166.         for(Object[] obj : result){  
  167.             rs.add(rowMapper.fromColumn(obj));  
  168.         }  
  169.         return rs;  
  170.     }  
  171.       
  172.       
  173.     /** 
  174.      * 按SQL查询对象列表. 
  175.      *  
  176.      * @param values 数量可变的参数,按顺序绑定. 
  177.      */  
  178.     @SuppressWarnings("unchecked")  
  179.     public <X> List<X> findBySQLRowMapper(RowMapper<X> rowMapper,final String sql, final Object... values) {  
  180.         Validate.notNull(rowMapper, "rowMapper不能为空!");  
  181.         List<Object[]> result = createSQLQuery(sql, values).list();  
  182.         return buildListResultFromRowMapper(rowMapper, result);  
  183.     }  
  184.       
  185.     /** 
  186.      * 按SQL查询对象列表,并将结果集转换成指定的对象列表 
  187.      *  
  188.      * @param values 数量可变的参数,按顺序绑定. 
  189.      */  
  190.     @SuppressWarnings("unchecked")  
  191.     public <X> List<X> findBySQL(final String sql, final Object... values) {  
  192.         return createSQLQuery(sql, values).list();  
  193.     }  
  194.   
  195.     /** 
  196.      * 按HQL查询对象列表. 
  197.      *  
  198.      * @param values 命名参数,按名称绑定. 
  199.      */  
  200.     @SuppressWarnings("unchecked")  
  201.     public <X> List<X> findByHQL(final String hql, final Map<String, ?> values) {  
  202.         return createHQLQuery(hql, values).list();  
  203.     }  
  204.       
  205.     /** 
  206.      * 按HQL查询对象列表,并将结果集封装成对象列表 
  207.      *  
  208.      * @param values 命名参数,按名称绑定. 
  209.      */  
  210.     @SuppressWarnings("unchecked")  
  211.     public <X> List<X> findByHQLRowMapper(RowMapper<X> rowMapper,final String hql, final Map<String, ?> values) {  
  212.         Validate.notNull(rowMapper, "rowMapper不能为空!");  
  213.         List<Object[]> result = createHQLQuery(hql, values).list();  
  214.         return buildListResultFromRowMapper(rowMapper, result);  
  215.     }  
  216.       
  217.     /** 
  218.      * 按SQL查询对象列表. 
  219.      * @param sql SQL查询语句 
  220.      * @param values 命名参数,按名称绑定. 
  221.      */  
  222.     @SuppressWarnings("unchecked")  
  223.     public <X> List<X> findBySQL(final String sql, final Map<String, ?> values) {  
  224.         return createSQLQuery(sql, values).list();  
  225.     }  
  226.       
  227.     /** 
  228.      * 查询在xxx.hbm.xml中配置的查询语句 
  229.      * @param queryName 查询的名称 
  230.      * @param parameters 参数 
  231.      * @return 
  232.      */  
  233.     public <X> List<X> findByNamedQuery(final String queryName, final Map<String, ?> parameters) {  
  234.         StatementTemplate statementTemplate = templateCache.get(queryName);  
  235.         String statement = processTemplate(statementTemplate,parameters);  
  236.         if(statementTemplate.getType() == StatementTemplate.TYPE.HQL){  
  237.             return this.findByHQL(statement);  
  238.         }else{  
  239.             return this.findBySQL(statement);  
  240.         }  
  241.     }  
  242.       
  243.     /** 
  244.      * 查询在xxx.hbm.xml中配置的查询语句 
  245.      * @param rowMapper 
  246.      * @param queryName 查询的名称 
  247.      * @param parameters 参数 
  248.      * @return 
  249.      */  
  250.     public <X> List<X> findByNamedQuery(RowMapper<X> rowMapper,final String queryName, final Map<String, ?> parameters) {  
  251.         StatementTemplate statementTemplate = templateCache.get(queryName);  
  252.         String statement = processTemplate(statementTemplate,parameters);  
  253.         if(statementTemplate.getType() == StatementTemplate.TYPE.HQL){  
  254.             return this.findByHQLRowMapper(rowMapper,statement);  
  255.         }else{  
  256.             return this.findBySQLRowMapper(rowMapper,statement);  
  257.         }  
  258.     }  
  259.       
  260.     /** 
  261.      * 按SQL查询对象列表,并将结果集封装成对象列表 
  262.      * @param sql SQL查询语句 
  263.      * @param values 命名参数,按名称绑定. 
  264.      */  
  265.     @SuppressWarnings("unchecked")  
  266.     public <X> List<X> findBySQLRowMapper(RowMapper<X> rowMapper,final String sql, final Map<String, ?> values) {  
  267.         Validate.notNull(rowMapper, "rowMapper不能为空!");  
  268.         List<Object[]> result = createSQLQuery(sql, values).list();  
  269.         return buildListResultFromRowMapper(rowMapper, result);  
  270.     }  
  271.   
  272.     /** 
  273.      * 按HQL查询唯一对象. 
  274.      *  
  275.      * @param values 数量可变的参数,按顺序绑定. 
  276.      */  
  277.     @SuppressWarnings("unchecked")  
  278.     public <X> X findUniqueByHQL(final String hql, final Object... values) {  
  279.         return (X) createHQLQuery(hql, values).uniqueResult();  
  280.     }  
  281.       
  282.     /** 
  283.      * 按SQL查询唯一对象. 
  284.      *  
  285.      * @param values 数量可变的参数,按顺序绑定. 
  286.      */  
  287.     @SuppressWarnings("unchecked")  
  288.     public <X> X findUniqueBySQL(final String sql, final Object... values) {  
  289.         return (X) createSQLQuery(sql, values).uniqueResult();  
  290.     }  
  291.   
  292.     /** 
  293.      * 按HQL查询唯一对象. 
  294.      *  
  295.      * @param values 命名参数,按名称绑定. 
  296.      */  
  297.     @SuppressWarnings("unchecked")  
  298.     public <X> X findUniqueByHQL(final String hql, final Map<String, ?> values) {  
  299.         return (X) createHQLQuery(hql, values).uniqueResult();  
  300.     }  
  301.       
  302.     /** 
  303.      * 按HQL查询唯一对象. 
  304.      * @param sql sql语句 
  305.      * @param values 命名参数,按名称绑定. 
  306.      */  
  307.     @SuppressWarnings("unchecked")  
  308.     public <X> X findUniqueBySQL(final String sql, final Map<String, ?> values) {  
  309.         return (X) createSQLQuery(sql, values).uniqueResult();  
  310.     }  
  311.   
  312.     /** 
  313.      * 执行HQL进行批量修改/删除操作. 
  314.      *  
  315.      * @param values 数量可变的参数,按顺序绑定. 
  316.      * @return 更新记录数. 
  317.      */  
  318.     public int batchExecuteHQL(final String hql, final Object... values) {  
  319.         return createHQLQuery(hql, values).executeUpdate();  
  320.     }  
  321.       
  322.     /** 
  323.      * 执行SQL进行批量修改/删除操作. 
  324.      *  
  325.      * @param sql sql语句 
  326.      * @param values 数量可变的参数,按顺序绑定. 
  327.      * @return 更新记录数. 
  328.      */  
  329.     public int batchExecuteSQL(final String sql, final Object... values) {  
  330.         return createSQLQuery(sql, values).executeUpdate();  
  331.     }  
  332.   
  333.     /** 
  334.      * 执行HQL进行批量修改/删除操作. 
  335.      *  
  336.      * @param values 命名参数,按名称绑定. 
  337.      * @return 更新记录数. 
  338.      */  
  339.     public int batchExecuteHQL(final String hql, final Map<String, ?> values) {  
  340.         return createHQLQuery(hql, values).executeUpdate();  
  341.     }  
  342.       
  343.     /** 
  344.      * 执行SQL进行批量修改/删除操作. 
  345.      *  
  346.      * @param values 命名参数,按名称绑定. 
  347.      * @return 更新记录数. 
  348.      */  
  349.     public int batchExecuteSQL(final String sql, final Map<String, ?> values) {  
  350.         return createSQLQuery(sql, values).executeUpdate();  
  351.     }  
  352.   
  353.     /** 
  354.      * 根据查询HQL与参数列表创建Query对象. 
  355.      * 与find()函数可进行更加灵活的操作. 
  356.      *  
  357.      * @param values 数量可变的参数,按顺序绑定. 
  358.      */  
  359.     public Query createHQLQuery(final String queryString, final Object... values) {  
  360.         Query query = getSession().createQuery(queryString);  
  361.         if (values != null) {  
  362.             for (int i = 0; i < values.length; i++) {  
  363.                 query.setParameter(i, values[i]);  
  364.             }  
  365.         }  
  366.         return query;  
  367.     }  
  368.       
  369.     /** 
  370.      * 根据查询SQL与参数列表创建Query对象. 
  371.      * 与find()函数可进行更加灵活的操作. 
  372.      * @param sqlQueryString sql语句 
  373.      *  
  374.      * @param values 数量可变的参数,按顺序绑定. 
  375.      */  
  376.     public Query createSQLQuery(final String sqlQueryString, final Object... values) {  
  377.         Query query = getSession().createSQLQuery(sqlQueryString);  
  378.         if (values != null) {  
  379.             for (int i = 0; i < values.length; i++) {  
  380.                 query.setParameter(i, values[i]);  
  381.             }  
  382.         }  
  383.         return query;  
  384.     }  
  385.   
  386.     /** 
  387.      * 根据查询HQL与参数列表创建Query对象. 
  388.      * 与find()函数可进行更加灵活的操作. 
  389.      *  
  390.      * @param values 命名参数,按名称绑定. 
  391.      */  
  392.     public Query createHQLQuery(final String queryString, final Map<String, ?> values) {  
  393.         Query query = getSession().createQuery(queryString);  
  394.         if (values != null) {  
  395.             query.setProperties(values);  
  396.         }  
  397.         return query;  
  398.     }  
  399.       
  400.     /** 
  401.      * 根据查询SQL与参数列表创建Query对象. 
  402.      * 与find()函数可进行更加灵活的操作. 
  403.      * @param queryString SQL语句 
  404.      * @param values 命名参数,按名称绑定. 
  405.      */  
  406.     public Query createSQLQuery(final String queryString, final Map<String, ?> values) {  
  407.         Query query = getSession().createSQLQuery(queryString);  
  408.         if (values != null) {  
  409.             query.setProperties(values);  
  410.         }  
  411.         return query;  
  412.     }  
  413.   
  414.     /** 
  415.      * 按Criteria查询对象列表. 
  416.      *  
  417.      * @param criterions 数量可变的Criterion. 
  418.      */  
  419.     @SuppressWarnings("unchecked")  
  420.     public List<T> find(final Criterion... criterions) {  
  421.         return createCriteria(criterions).list();  
  422.     }  
  423.   
  424.     /** 
  425.      * 按Criteria查询唯一对象. 
  426.      *  
  427.      * @param criterions 数量可变的Criterion. 
  428.      */  
  429.     @SuppressWarnings("unchecked")  
  430.     public T findUnique(final Criterion... criterions) {  
  431.         return (T) createCriteria(criterions).uniqueResult();  
  432.     }  
  433.   
  434.     /** 
  435.      * 根据Criterion条件创建Criteria. 
  436.      * 与find()函数可进行更加灵活的操作. 
  437.      *  
  438.      * @param criterions 数量可变的Criterion. 
  439.      */  
  440.     public Criteria createCriteria(final Criterion... criterions) {  
  441.         Criteria criteria = getSession().createCriteria(entityClass);  
  442.         for (Criterion c : criterions) {  
  443.             criteria.add(c);  
  444.         }  
  445.         return criteria;  
  446.     }  
  447.   
  448.     /** 
  449.      * 初始化对象. 
  450.      * 使用load()方法得到的仅是对象Proxy, 在传到View层前需要进行初始化. 
  451.      * 如果传入entity, 则只初始化entity的直接属性,但不会初始化延迟加载的关联集合和属性. 
  452.      * 如需初始化关联属性,需执行: 
  453.      * Hibernate.initialize(user.getRoles()),初始化User的直接属性和关联集合. 
  454.      * Hibernate.initialize(user.getDescription()),初始化User的直接属性和延迟加载的Description属性. 
  455.      */  
  456.     public void initProxyObject(Object proxy) {  
  457.         Hibernate.initialize(proxy);  
  458.     }  
  459.   
  460.     /** 
  461.      * Flush当前Session. 
  462.      */  
  463.     public void flush() {  
  464.         getSession().flush();  
  465.     }  
  466.   
  467.     /** 
  468.      * 为Query添加distinct transformer. 
  469.      * 预加载关联对象的HQL会引起主对象重复, 需要进行distinct处理. 
  470.      */  
  471.     public Query distinct(Query query) {  
  472.         query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);  
  473.         return query;  
  474.     }  
  475.   
  476.     /** 
  477.      * 为Criteria添加distinct transformer. 
  478.      * 预加载关联对象的HQL会引起主对象重复, 需要进行distinct处理. 
  479.      */  
  480.     public Criteria distinct(Criteria criteria) {  
  481.         criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);  
  482.         return criteria;  
  483.     }  
  484.   
  485.     /** 
  486.      * 取得对象的主键名. 
  487.      */  
  488.     public String getIdName() {  
  489.         ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);  
  490.         return meta.getIdentifierPropertyName();  
  491.     }  
  492.   
  493.     /** 
  494.      * 判断对象的属性值在数据库内是否唯一. 
  495.      *  
  496.      * 在修改对象的情景下,如果属性新修改的值(value)等于属性原来的值(orgValue)则不作比较. 
  497.      */  
  498.     public boolean isPropertyUnique(final String propertyName, final Object newValue, final Object oldValue) {  
  499.         if (newValue == null || newValue.equals(oldValue)) {  
  500.             return true;  
  501.         }  
  502.         Object object = findUniqueBy(propertyName, newValue);  
  503.         return (object == null);  
  504.     }  
  505.   
  506.     @Override  
  507.     public void update(T object) {  
  508.         getSession().update(object);  
  509.     }  
  510.   
  511.     @SuppressWarnings("unchecked")  
  512.     @Override  
  513.     public T load(ID id) {  
  514.         return (T) getSession().load(this.entityClass, id);  
  515.     }  
  516.       
  517.     /** 
  518.      * 将list转化为数组 
  519.      * @param list 
  520.      * @return 
  521.      */  
  522.     protected Criterion[] list2Array(List<Criterion> list){  
  523.         if(list == null){  
  524.             return new Criterion[0];  
  525.         }  
  526.         Criterion[] result = new Criterion[list.size()];  
  527.         for(int i = 0; i < list.size(); i++){  
  528.             result[i] = list.get(i);  
  529.         }  
  530.         return result;  
  531.     }  
  532.   
  533.     @Override  
  534.     public void afterPropertiesSet() throws Exception {  
  535.         templateCache = new HashMap<String, StatementTemplate>();  
  536.         if(this.dynamicStatementBuilder == null){  
  537.             this.dynamicStatementBuilder = new NoneDynamicHibernateStatementBuilder();  
  538.         }  
  539.         dynamicStatementBuilder.init();  
  540.         Map<String,String> namedHQLQueries = dynamicStatementBuilder.getNamedHQLQueries();  
  541.         Map<String,String> namedSQLQueries = dynamicStatementBuilder.getNamedSQLQueries();  
  542.         Configuration configuration = new Configuration();  
  543.         configuration.setNumberFormat("#");  
  544.         StringTemplateLoader stringLoader = new StringTemplateLoader();  
  545.         for(Entry<String, String> entry : namedHQLQueries.entrySet()){  
  546.             stringLoader.putTemplate(entry.getKey(), entry.getValue());  
  547.             templateCache.put(entry.getKey(), new StatementTemplate(StatementTemplate.TYPE.HQL,new Template(entry.getKey(),new StringReader(entry.getValue()),configuration)));  
  548.         }  
  549.         for(Entry<String, String> entry : namedSQLQueries.entrySet()){  
  550.             stringLoader.putTemplate(entry.getKey(), entry.getValue());  
  551.             templateCache.put(entry.getKey(), new StatementTemplate(StatementTemplate.TYPE.SQL,new Template(entry.getKey(),new StringReader(entry.getValue()),configuration)));  
  552.         }  
  553.         configuration.setTemplateLoader(stringLoader);  
  554.     }  
  555.       
  556.     protected String processTemplate(StatementTemplate statementTemplate,Map<String, ?> parameters){  
  557.         StringWriter stringWriter = new StringWriter();  
  558.         try {  
  559.             statementTemplate.getTemplate().process(parameters, stringWriter);  
  560.         } catch (Exception e) {  
  561.             LOGER.error("处理DAO查询参数模板时发生错误:{}",e.toString());  
  562.             throw new SysException(e);  
  563.         }  
  564.         return stringWriter.toString();  
  565.     }  
  566. }  

我们的SimpleHibernateDAO实现了InitializingBean,在其afterProperties方法中我们将调用DynamicHibernateStatementBuilder把语句缓存起来
上层方法调用示例-这个已经非常类似ibatis了

[java] view plaincopyprint?
 
  1. public List<Resource> getDescendants(Long userId,String code) {  
  2.     Map<String, Object> values = new HashMap<String, Object>();  
  3.     values.put("userId", String.valueOf(userId));  
  4.     values.put("code", code);  
  5.     values.put("type", String.valueOf(ResourceTypeEnum.URL_RESOURCE.getType()));  
  6.     values.put("status", String.valueOf(ResourceStatusEnum.ACTIVE.getStatus()));  
  7.     return this.findByNamedQuery(new ResourceRowMapper(),"resource.getDescendants", values);  
  8. }