1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.orm.hibernate;
  17. import java.io.Serializable;
  18. import java.sql.SQLException;
  19. import java.util.Collection;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import net.sf.hibernate.Criteria;
  23. import net.sf.hibernate.FlushMode;
  24. import net.sf.hibernate.HibernateException;
  25. import net.sf.hibernate.LockMode;
  26. import net.sf.hibernate.Query;
  27. import net.sf.hibernate.Session;
  28. import net.sf.hibernate.SessionFactory;
  29. import net.sf.hibernate.type.Type;
  30. import org.springframework.dao.DataAccessException;
  31. import org.springframework.transaction.support.TransactionSynchronizationManager;
  32. /**
  33. * Helper class that simplifies Hibernate data access code, and converts
  34. * checked HibernateExceptions into unchecked DataAccessExceptions,
  35. * compatible to the org.springframework.dao exception hierarchy.
  36. * Uses the same SQLExceptionTranslator mechanism as JdbcTemplate.
  37. *
  38. * <p>Typically used to implement data access or business logic services that
  39. * use Hibernate within their implementation but are Hibernate-agnostic in
  40. * their interface. The latter resp. code calling the latter only have to deal
  41. * with business objects, query objects, and org.springframework.dao exceptions.
  42. *
  43. * <p>The central method is "execute", supporting Hibernate code implementing
  44. * the HibernateCallback interface. It provides Hibernate Session handling
  45. * such that neither the HibernateCallback implementation nor the calling
  46. * code needs to explicitly care about retrieving/closing Hibernate Sessions,
  47. * or handling Session lifecycle exceptions. For typical single step actions,
  48. * there are various convenience methods (find, load, saveOrUpdate, delete).
  49. *
  50. * <p>Can be used within a service implementation via direct instantiation
  51. * with a SessionFactory reference, or get prepared in an application context
  52. * and given to services as bean reference. Note: The SessionFactory should
  53. * always be configured as bean in the application context, in the first case
  54. * given to the service directly, in the second case to the prepared template.
  55. *
  56. * <p>This class can be considered a programmatic alternative to
  57. * HibernateInterceptor. The major advantage is its straightforwardness, the
  58. * major disadvantage that no checked application exceptions can get thrown
  59. * from within data access code. Respective checks and the actual throwing of
  60. * such exceptions can often be deferred to after callback execution, though.
  61. *
  62. * <p>Note that even if HibernateTransactionManager is used for transaction
  63. * demarcation in higher-level services, all those services above the data
  64. * access layer don't need need to be Hibernate-aware. Setting such a special
  65. * PlatformTransactionManager is a configuration issue, without introducing
  66. * code dependencies. For example, switching to JTA is just a matter of
  67. * Spring configuration (use JtaTransactionManager instead), without needing
  68. * to touch application code.
  69. *
  70. * <p>LocalSessionFactoryBean is the preferred way of obtaining a reference
  71. * to a specific Hibernate SessionFactory, at least in a non-EJB environment.
  72. *
  73. * <p>Note: Spring's Hibernate support requires Hibernate 2.1 (as of Spring 1.0).
  74. *
  75. * @author Juergen Hoeller
  76. * @since 02.05.2003
  77. * @see HibernateCallback
  78. * @see HibernateInterceptor
  79. * @see HibernateTransactionManager
  80. * @see LocalSessionFactoryBean
  81. * @see org.springframework.jndi.JndiObjectFactoryBean
  82. * @see net.sf.hibernate.Session
  83. */
  84. public class HibernateTemplate extends HibernateAccessor implements HibernateOperations {
  85. private boolean allowCreate = true;
  86. /**
  87. * Create a new HibernateTemplate instance.
  88. */
  89. public HibernateTemplate() {
  90. }
  91. /**
  92. * Create a new HibernateTemplate instance.
  93. * @param sessionFactory SessionFactory to create Sessions
  94. */
  95. public HibernateTemplate(SessionFactory sessionFactory) {
  96. setSessionFactory(sessionFactory);
  97. afterPropertiesSet();
  98. }
  99. /**
  100. * Create a new HibernateTemplate instance.
  101. * @param sessionFactory SessionFactory to create Sessions
  102. * @param allowCreate if a new Session should be created
  103. * if no thread-bound found
  104. */
  105. public HibernateTemplate(SessionFactory sessionFactory, boolean allowCreate) {
  106. setSessionFactory(sessionFactory);
  107. setAllowCreate(allowCreate);
  108. afterPropertiesSet();
  109. }
  110. /**
  111. * Set if a new Session should be created if no thread-bound found.
  112. * <p>HibernateTemplate is aware of a respective Session bound to the
  113. * current thread, for example when using HibernateTransactionManager.
  114. * If allowCreate is true, a new Session will be created if none found.
  115. * If false, an IllegalStateException will get thrown in this case.
  116. * @see SessionFactoryUtils#getSession(SessionFactory, boolean)
  117. */
  118. public void setAllowCreate(boolean allowCreate) {
  119. this.allowCreate = allowCreate;
  120. }
  121. /**
  122. * Return if a new Session should be created if no thread-bound found.
  123. */
  124. public boolean isAllowCreate() {
  125. return allowCreate;
  126. }
  127. public Object execute(HibernateCallback action) throws DataAccessException {
  128. Session session = (!this.allowCreate ?
  129. SessionFactoryUtils.getSession(getSessionFactory(), false) :
  130. SessionFactoryUtils.getSession(getSessionFactory(), getEntityInterceptor(),
  131. getJdbcExceptionTranslator()));
  132. boolean existingTransaction = TransactionSynchronizationManager.hasResource(getSessionFactory());
  133. if (!existingTransaction && getFlushMode() == FLUSH_NEVER) {
  134. session.setFlushMode(FlushMode.NEVER);
  135. }
  136. try {
  137. Object result = action.doInHibernate(session);
  138. flushIfNecessary(session, existingTransaction);
  139. return result;
  140. }
  141. catch (HibernateException ex) {
  142. throw convertHibernateAccessException(ex);
  143. }
  144. catch (SQLException ex) {
  145. throw convertJdbcAccessException(ex);
  146. }
  147. catch (RuntimeException ex) {
  148. // callback code threw application exception
  149. throw ex;
  150. }
  151. finally {
  152. SessionFactoryUtils.closeSessionIfNecessary(session, getSessionFactory());
  153. }
  154. }
  155. public List executeFind(HibernateCallback action) throws DataAccessException {
  156. return (List) execute(action);
  157. }
  158. //-------------------------------------------------------------------------
  159. // Convenience methods for load, save, update, delete
  160. //-------------------------------------------------------------------------
  161. public Object get(final Class entityClass, final Serializable id) throws DataAccessException {
  162. return execute(new HibernateCallback() {
  163. public Object doInHibernate(Session session) throws HibernateException {
  164. return session.get(entityClass, id);
  165. }
  166. });
  167. }
  168. public Object get(final Class entityClass, final Serializable id, final LockMode lockMode)
  169. throws DataAccessException {
  170. return execute(new HibernateCallback() {
  171. public Object doInHibernate(Session session) throws HibernateException {
  172. return session.get(entityClass, id, lockMode);
  173. }
  174. });
  175. }
  176. public Object load(final Class entityClass, final Serializable id) throws DataAccessException {
  177. return execute(new HibernateCallback() {
  178. public Object doInHibernate(Session session) throws HibernateException {
  179. return session.load(entityClass, id);
  180. }
  181. });
  182. }
  183. public Object load(final Class entityClass, final Serializable id, final LockMode lockMode)
  184. throws DataAccessException {
  185. return execute(new HibernateCallback() {
  186. public Object doInHibernate(Session session) throws HibernateException {
  187. return session.load(entityClass, id, lockMode);
  188. }
  189. });
  190. }
  191. public List loadAll(final Class entityClass) throws DataAccessException {
  192. return executeFind(new HibernateCallback() {
  193. public Object doInHibernate(Session session) throws HibernateException {
  194. Criteria criteria = createCriteria(session, entityClass);
  195. return criteria.list();
  196. }
  197. });
  198. }
  199. public void evict(final Object entity) throws DataAccessException {
  200. execute(new HibernateCallback() {
  201. public Object doInHibernate(Session session) throws HibernateException {
  202. session.evict(entity);
  203. return null;
  204. }
  205. });
  206. }
  207. public void lock(final Object entity, final LockMode lockMode) throws DataAccessException {
  208. execute(new HibernateCallback() {
  209. public Object doInHibernate(Session session) throws HibernateException {
  210. session.lock(entity, lockMode);
  211. return null;
  212. }
  213. });
  214. }
  215. public Serializable save(final Object entity) throws DataAccessException {
  216. return (Serializable) execute(new HibernateCallback() {
  217. public Object doInHibernate(Session session) throws HibernateException {
  218. return session.save(entity);
  219. }
  220. });
  221. }
  222. public void save(final Object entity, final Serializable id) throws DataAccessException {
  223. execute(new HibernateCallback() {
  224. public Object doInHibernate(Session session) throws HibernateException {
  225. session.save(entity, id);
  226. return null;
  227. }
  228. });
  229. }
  230. public void saveOrUpdate(final Object entity) throws DataAccessException {
  231. execute(new HibernateCallback() {
  232. public Object doInHibernate(Session session) throws HibernateException {
  233. session.saveOrUpdate(entity);
  234. return null;
  235. }
  236. });
  237. }
  238. public Object saveOrUpdateCopy(final Object entity) throws DataAccessException {
  239. return execute(new HibernateCallback() {
  240. public Object doInHibernate(Session session) throws HibernateException {
  241. return session.saveOrUpdateCopy(entity);
  242. }
  243. });
  244. }
  245. public void update(final Object entity) throws DataAccessException {
  246. execute(new HibernateCallback() {
  247. public Object doInHibernate(Session session) throws HibernateException {
  248. session.update(entity);
  249. return null;
  250. }
  251. });
  252. }
  253. public void update(final Object entity, final LockMode lockMode) throws DataAccessException {
  254. execute(new HibernateCallback() {
  255. public Object doInHibernate(Session session) throws HibernateException {
  256. session.update(entity);
  257. session.lock(entity, lockMode);
  258. return null;
  259. }
  260. });
  261. }
  262. public void delete(final Object entity) throws DataAccessException {
  263. execute(new HibernateCallback() {
  264. public Object doInHibernate(Session session) throws HibernateException {
  265. session.delete(entity);
  266. return null;
  267. }
  268. });
  269. }
  270. public void delete(final Object entity, final LockMode lockMode) throws DataAccessException {
  271. execute(new HibernateCallback() {
  272. public Object doInHibernate(Session session) throws HibernateException {
  273. session.lock(entity, lockMode);
  274. session.delete(entity);
  275. return null;
  276. }
  277. });
  278. }
  279. public void deleteAll(final Collection entities) throws DataAccessException {
  280. execute(new HibernateCallback() {
  281. public Object doInHibernate(Session session) throws HibernateException {
  282. for (Iterator it = entities.iterator(); it.hasNext();) {
  283. session.delete(it.next());
  284. }
  285. return null;
  286. }
  287. });
  288. }
  289. //-------------------------------------------------------------------------
  290. // Convenience finder methods
  291. //-------------------------------------------------------------------------
  292. public List find(final String queryString) throws DataAccessException {
  293. return executeFind(new HibernateCallback() {
  294. public Object doInHibernate(Session session) throws HibernateException {
  295. Query queryObject = createQuery(session, queryString);
  296. return queryObject.list();
  297. }
  298. });
  299. }
  300. public List find(final String queryString, final Object value) throws DataAccessException {
  301. return executeFind(new HibernateCallback() {
  302. public Object doInHibernate(Session session) throws HibernateException {
  303. Query queryObject = createQuery(session, queryString);
  304. queryObject.setParameter(0, value);
  305. return queryObject.list();
  306. }
  307. });
  308. }
  309. public List find(final String queryString, final Object value, final Type type)
  310. throws DataAccessException {
  311. return executeFind(new HibernateCallback() {
  312. public Object doInHibernate(Session session) throws HibernateException {
  313. Query queryObject = createQuery(session, queryString);
  314. queryObject.setParameter(0, value, type);
  315. return queryObject.list();
  316. }
  317. });
  318. }
  319. public List find(final String queryString, final Object[] values) throws DataAccessException {
  320. return executeFind(new HibernateCallback() {
  321. public Object doInHibernate(Session session) throws HibernateException {
  322. Query queryObject = createQuery(session, queryString);
  323. for (int i = 0; i < values.length; i++) {
  324. Object value = values[i];
  325. queryObject.setParameter(i, value);
  326. }
  327. return queryObject.list();
  328. }
  329. });
  330. }
  331. public List find(final String queryString, final Object[] values, final Type[] types)
  332. throws DataAccessException {
  333. if (values.length != types.length) {
  334. throw new IllegalArgumentException("Length of values array must match length of types array");
  335. }
  336. return executeFind(new HibernateCallback() {
  337. public Object doInHibernate(Session session) throws HibernateException {
  338. Query queryObject = createQuery(session, queryString);
  339. for (int i = 0; i < values.length; i++) {
  340. Object value = values[i];
  341. queryObject.setParameter(i, value, types[i]);
  342. }
  343. return queryObject.list();
  344. }
  345. });
  346. }
  347. public List findByValueBean(final String queryString, final Object valueBean)
  348. throws DataAccessException {
  349. return executeFind(new HibernateCallback() {
  350. public Object doInHibernate(Session session) throws HibernateException {
  351. Query queryObject = createQuery(session, queryString);
  352. queryObject.setProperties(valueBean);
  353. return queryObject.list();
  354. }
  355. });
  356. }
  357. public List findByNamedQuery(final String queryName) throws DataAccessException {
  358. return executeFind(new HibernateCallback() {
  359. public Object doInHibernate(Session session) throws HibernateException {
  360. Query queryObject = getNamedQuery(session, queryName);
  361. return queryObject.list();
  362. }
  363. });
  364. }
  365. public List findByNamedQuery(final String queryName, final Object value)
  366. throws DataAccessException {
  367. return executeFind(new HibernateCallback() {
  368. public Object doInHibernate(Session session) throws HibernateException {
  369. Query queryObject = getNamedQuery(session, queryName);
  370. queryObject.setParameter(0, value);
  371. return queryObject.list();
  372. }
  373. });
  374. }
  375. public List findByNamedQuery(final String queryName, final Object value, final Type type)
  376. throws DataAccessException {
  377. return executeFind(new HibernateCallback() {
  378. public Object doInHibernate(Session session) throws HibernateException {
  379. Query queryObject = getNamedQuery(session, queryName);
  380. queryObject.setParameter(0, value, type);
  381. return queryObject.list();
  382. }
  383. });
  384. }
  385. public List findByNamedQuery(final String queryName, final Object[] values)
  386. throws DataAccessException {
  387. return executeFind(new HibernateCallback() {
  388. public Object doInHibernate(Session session) throws HibernateException {
  389. Query queryObject = getNamedQuery(session, queryName);
  390. for (int i = 0; i < values.length; i++) {
  391. Object value = values[i];
  392. queryObject.setParameter(i, value);
  393. }
  394. return queryObject.list();
  395. }
  396. });
  397. }
  398. public List findByNamedQuery(final String queryName, final Object[] values, final Type[] types)
  399. throws DataAccessException {
  400. if (values.length != types.length) {
  401. throw new IllegalArgumentException("Length of values array must match length of types array");
  402. }
  403. return executeFind(new HibernateCallback() {
  404. public Object doInHibernate(Session session) throws HibernateException {
  405. Query queryObject = getNamedQuery(session, queryName);
  406. for (int i = 0; i < values.length; i++) {
  407. Object value = values[i];
  408. queryObject.setParameter(i, value, types[i]);
  409. }
  410. return queryObject.list();
  411. }
  412. });
  413. }
  414. public List findByNamedQuery(final String queryName, final String paramName, final Object value)
  415. throws DataAccessException {
  416. return executeFind(new HibernateCallback() {
  417. public Object doInHibernate(Session session) throws HibernateException {
  418. Query queryObject = getNamedQuery(session, queryName);
  419. queryObject.setParameter(paramName, value);
  420. return queryObject.list();
  421. }
  422. });
  423. }
  424. public List findByNamedQuery(final String queryName, final String paramName, final Object value, final Type type)
  425. throws DataAccessException {
  426. return executeFind(new HibernateCallback() {
  427. public Object doInHibernate(Session session) throws HibernateException {
  428. Query queryObject = getNamedQuery(session, queryName);
  429. queryObject.setParameter(paramName, value, type);
  430. return queryObject.list();
  431. }
  432. });
  433. }
  434. public List findByNamedQuery(final String queryName, final String[] paramNames, final Object[] values)
  435. throws DataAccessException {
  436. if (paramNames.length != values.length) {
  437. throw new IllegalArgumentException("Length of parmNames array must match length of values array");
  438. }
  439. return executeFind(new HibernateCallback() {
  440. public Object doInHibernate(Session session) throws HibernateException {
  441. Query queryObject = getNamedQuery(session, queryName);
  442. for (int i = 0; i < values.length; i++) {
  443. Object value = values[i];
  444. queryObject.setParameter(paramNames[i], value);
  445. }
  446. return queryObject.list();
  447. }
  448. });
  449. }
  450. public List findByNamedQuery(final String queryName, final String[] paramNames, final Object[] values,
  451. final Type[] types) throws DataAccessException {
  452. if (paramNames.length != values.length) {
  453. throw new IllegalArgumentException("Length of parmNames array must match length of values array");
  454. }
  455. if (values.length != types.length) {
  456. throw new IllegalArgumentException("Length of values array must match length of types array");
  457. }
  458. return executeFind(new HibernateCallback() {
  459. public Object doInHibernate(Session session) throws HibernateException {
  460. Query queryObject = getNamedQuery(session, queryName);
  461. for (int i = 0; i < values.length; i++) {
  462. Object value = values[i];
  463. queryObject.setParameter(paramNames[i], value, types[i]);
  464. }
  465. return queryObject.list();
  466. }
  467. });
  468. }
  469. public List findByNamedQueryAndValueBean(final String queryName, final Object valueBean)
  470. throws DataAccessException {
  471. return executeFind(new HibernateCallback() {
  472. public Object doInHibernate(Session session) throws HibernateException {
  473. Query queryObject = getNamedQuery(session, queryName);
  474. queryObject.setProperties(valueBean);
  475. return queryObject.list();
  476. }
  477. });
  478. }
  479. /**
  480. * Create a Query object for the given Session and the given query string.
  481. * To be used within a HibernateCallback.
  482. * <p>Applies a transaction timeout, if any. If you don't use such timeouts,
  483. * the call is equivalent to Session.createQuery.
  484. * @param session current Hibernate Session
  485. * @param queryString the HQL query string
  486. * @return the Query object
  487. * @throws HibernateException if the Query could not be created
  488. * @see HibernateCallback#doInHibernate
  489. * @see net.sf.hibernate.Session#createQuery
  490. */
  491. public Query createQuery(Session session, String queryString) throws HibernateException {
  492. Query queryObject = session.createQuery(queryString);
  493. SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
  494. return queryObject;
  495. }
  496. /**
  497. * Create a named Query object for the given Session and the given query name.
  498. * To be used within a HibernateCallback.
  499. * <p>Applies a transaction timeout, if any. If you don't use such timeouts,
  500. * the call is equivalent to Session.getNamedQuery.
  501. * @param session current Hibernate Session
  502. * @param queryName the name of the query in the Hibernate mapping file
  503. * @return the Query object
  504. * @throws HibernateException if the Query could not be created
  505. * @see HibernateCallback#doInHibernate
  506. * @see net.sf.hibernate.Session#getNamedQuery
  507. */
  508. public Query getNamedQuery(Session session, String queryName) throws HibernateException {
  509. Query queryObject = session.getNamedQuery(queryName);
  510. SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
  511. return queryObject;
  512. }
  513. /**
  514. * Create a Criteria object for the given Session and the given entity class.
  515. * To be used within a HibernateCallback.
  516. * <p>Applies a transaction timeout, if any. If you don't use such timeouts,
  517. * the call is equivalent to Session.createCriteria.
  518. * @param session current Hibernate Session
  519. * @param entityClass the entity class to create the Criteria for
  520. * @return the Query object
  521. * @throws HibernateException if the Criteria could not be created
  522. * @see HibernateCallback#doInHibernate
  523. * @see net.sf.hibernate.Session#createCriteria
  524. */
  525. public Criteria createCriteria(Session session, Class entityClass) throws HibernateException {
  526. Criteria criteria = session.createCriteria(entityClass);
  527. SessionFactoryUtils.applyTransactionTimeout(criteria, getSessionFactory());
  528. return criteria;
  529. }
  530. }