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. }