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


 


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属性和传入查询参数来得到最终解析出来的语句

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



 


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属性用来唯一标示该语句,如下


 

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. >  
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. >  
24.   
25. <!-- The sql-query element declares a named SQL query string -->  
26.   
27. <!ELEMENT sql-query (#PCDATA)>  
28. >



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

编写DTD校验器

 


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. null; // returning null triggers default behavior  
13. if ( systemId != null ) {  
14. "trying to resolve system-id [" + systemId + "]" );  
15. if ( systemId.startsWith( HOP_DYNAMIC_STATEMENT ) ) {  
16. "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. null;  
25. "com/haier/openplatform/dao/hibernate/" + systemId.substring( namespace.length() );  
26.         InputStream dtdStream = resolveInHibernateNamespace( path );  
27. if ( dtdStream == null ) {  
28. "unable to locate [" + systemId + "] on classpath" );  
29. if ( systemId.substring( namespace.length() ).indexOf( "2.0" ) > -1 ) {  
30. "Don't use old DTDs, read the Hibernate 3.x Migration Guide!" );  
31.             }  
32.         }  
33. else {  
34. "located [" + systemId + "] in classpath" );  
35. 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文件



 


    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:具体的)



     


    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语句加载到内存中




      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. new HashMap<String, String>();  
      34. new HashMap<String, String>();  
      35. boolean flag = this.resourceLoader instanceof ResourcePatternResolver;  
      36. for (String file : fileNames) {  
      37. if (flag) {  
      38. 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. null;  
      66. try {  
      67. 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. "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. }



      配置一下



       


      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层代码



       



        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. "entity不能为空");  
        57.         getSession().save(entity);  
        58. "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. "delete entity: {}", entity);  
        73.     }  
        74.   
        75. /**
        76.      * 按id删除对象.
        77.      */  
        78. @Override  
        79. public void delete(final ID id) {  
        80. "id不能为空");  
        81.         delete(get(id));  
        82. "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. "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. "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. 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. "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. "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. "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. 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. 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. new Configuration();  
        543. "#");  
        544. new StringTemplateLoader();  
        545. for(Entry<String, String> entry : namedHQLQueries.entrySet()){  
        546.             stringLoader.putTemplate(entry.getKey(), entry.getValue());  
        547. 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. 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. new StringWriter();  
        558. try {  
        559.             statementTemplate.getTemplate().process(parameters, stringWriter);  
        560. catch (Exception e) {  
        561. "处理DAO查询参数模板时发生错误:{}",e.toString());  
        562. throw new SysException(e);  
        563.         }  
        564. return stringWriter.toString();  
        565.     }  
        566. }



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


         


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