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.sql.SQLException;
  18. import net.sf.hibernate.HibernateException;
  19. import net.sf.hibernate.Interceptor;
  20. import net.sf.hibernate.JDBCException;
  21. import net.sf.hibernate.Session;
  22. import net.sf.hibernate.SessionFactory;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;
  25. import org.springframework.beans.factory.InitializingBean;
  26. import org.springframework.core.Constants;
  27. import org.springframework.dao.DataAccessException;
  28. import org.springframework.jdbc.support.SQLExceptionTranslator;
  29. import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
  30. /**
  31. * Base class for HibernateTemplate and HibernateInterceptor, defining common
  32. * properties like flushing behavior.
  33. *
  34. * <p>Not intended to be used directly. See HibernateTemplate and HibernateInterceptor.
  35. *
  36. * @author Juergen Hoeller
  37. * @since 29.07.2003
  38. * @see HibernateTemplate
  39. * @see HibernateInterceptor
  40. * @see #setFlushMode
  41. */
  42. public abstract class HibernateAccessor implements InitializingBean {
  43. /**
  44. * Never flush is a good strategy for read-only units of work.
  45. * Hibernate will not track and look for changes in this case,
  46. * avoiding any overhead of modification detection.
  47. * @see #setFlushMode
  48. */
  49. public static final int FLUSH_NEVER = 0;
  50. /**
  51. * Automatic flushing is the default mode for a Hibernate session.
  52. * A session will get flushed on transaction commit or session closing,
  53. * and on certain find operations that might involve already modified
  54. * instances, but not after each unit of work like with eager flushing.
  55. * @see #setFlushMode
  56. */
  57. public static final int FLUSH_AUTO = 1;
  58. /**
  59. * Eager flushing leads to immediate synchronization with the database,
  60. * even if in a transaction. This causes inconsistencies to show up and throw
  61. * a respective exception immediately, and JDBC access code that participates
  62. * in the same transaction will see the changes as the database is already
  63. * aware of them then. But the drawbacks are:
  64. * <ul>
  65. * <li>additional communication roundtrips with the database, instead of a
  66. * single batch at transaction commit;
  67. * <li>the fact that an actual database rollback is needed if the Hibernate
  68. * transaction rolls back (due to already submitted SQL statements).
  69. * </ul>
  70. * @see #setFlushMode
  71. */
  72. public static final int FLUSH_EAGER = 2;
  73. protected final Log logger = LogFactory.getLog(getClass());
  74. /** Constants instance for HibernateAccessor */
  75. private static final Constants constants = new Constants(HibernateAccessor.class);
  76. private SessionFactory sessionFactory;
  77. private Interceptor entityInterceptor;
  78. private SQLExceptionTranslator jdbcExceptionTranslator = new SQLStateSQLExceptionTranslator();
  79. private int flushMode = FLUSH_AUTO;
  80. /**
  81. * Set the Hibernate SessionFactory that should be used to create
  82. * Hibernate Sessions.
  83. */
  84. public void setSessionFactory(SessionFactory sessionFactory) {
  85. this.sessionFactory = sessionFactory;
  86. }
  87. /**
  88. * Return the Hibernate SessionFactory that should be used to create
  89. * Hibernate Sessions.
  90. */
  91. public SessionFactory getSessionFactory() {
  92. return sessionFactory;
  93. }
  94. /**
  95. * Set a Hibernate entity interceptor that allows to inspect and change
  96. * property values before writing to and reading from the database.
  97. * Will get applied to any <b>new</b> Session created by this object.
  98. * <p>Such an interceptor can either be set at the SessionFactory level,
  99. * i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on
  100. * HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager.
  101. * It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager
  102. * to avoid repeated configuration and guarantee consistent behavior in transactions.
  103. * @see LocalSessionFactoryBean#setEntityInterceptor
  104. * @see HibernateTransactionManager#setEntityInterceptor
  105. */
  106. public void setEntityInterceptor(Interceptor entityInterceptor) {
  107. this.entityInterceptor = entityInterceptor;
  108. }
  109. /**
  110. * Return the current Hibernate entity interceptor, or null if none.
  111. */
  112. public Interceptor getEntityInterceptor() {
  113. return entityInterceptor;
  114. }
  115. /**
  116. * Set the JDBC exception translator for this instance.
  117. * Applied to SQLExceptions thrown by callback code, be it direct
  118. * SQLExceptions or wrapped HibernateJDBCExceptions.
  119. * <p>The default exception translator evaluates the exception's SQLState.
  120. * @param jdbcExceptionTranslator exception translator
  121. * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
  122. * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
  123. */
  124. public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
  125. this.jdbcExceptionTranslator = jdbcExceptionTranslator;
  126. }
  127. /**
  128. * Return the JDBC exception translator for this instance.
  129. */
  130. public SQLExceptionTranslator getJdbcExceptionTranslator() {
  131. return this.jdbcExceptionTranslator;
  132. }
  133. /**
  134. * Set the flush behavior by the name of the respective constant
  135. * in this class, e.g. "FLUSH_AUTO". Default is FLUSH_AUTO.
  136. * Will get applied to any <b>new</b> Session created by this object.
  137. * @param constantName name of the constant
  138. * @see #setFlushMode
  139. * @see #FLUSH_AUTO
  140. */
  141. public void setFlushModeName(String constantName) {
  142. setFlushMode(constants.asNumber(constantName).intValue());
  143. }
  144. /**
  145. * Set the flush behavior to one of the constants in this class.
  146. * Default is FLUSH_AUTO. Will get applied to any <b>new</b> Session
  147. * created by this object.
  148. * @see #setFlushModeName
  149. * @see #FLUSH_AUTO
  150. */
  151. public void setFlushMode(int flushMode) {
  152. this.flushMode = flushMode;
  153. }
  154. /**
  155. * Return if a flush should be forced after executing the callback code.
  156. */
  157. public int getFlushMode() {
  158. return flushMode;
  159. }
  160. public void afterPropertiesSet() {
  161. if (this.sessionFactory == null) {
  162. throw new IllegalArgumentException("sessionFactory is required");
  163. }
  164. }
  165. /**
  166. * Flush the given Hibernate session if necessary.
  167. * @param session the current Hibernate session
  168. * @param existingTransaction if executing within an existing transaction
  169. * @throws HibernateException in case of Hibernate flushing errors
  170. */
  171. public void flushIfNecessary(Session session, boolean existingTransaction) throws HibernateException {
  172. if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() == FLUSH_AUTO)) {
  173. logger.debug("Eagerly flushing Hibernate session");
  174. session.flush();
  175. }
  176. }
  177. /**
  178. * Convert the given HibernateException to an appropriate exception from
  179. * the org.springframework.dao hierarchy. Will automatically detect
  180. * wrapped SQLExceptions and convert them accordingly.
  181. * <p>The default implementation delegates to SessionFactoryUtils
  182. * and convertJdbcAccessException. Can be overridden in subclasses.
  183. * @param ex HibernateException that occured
  184. * @return the corresponding DataAccessException instance
  185. * @see #convertJdbcAccessException
  186. * @see SessionFactoryUtils#convertHibernateAccessException
  187. */
  188. public DataAccessException convertHibernateAccessException(HibernateException ex) {
  189. if (ex instanceof JDBCException) {
  190. return convertJdbcAccessException(((JDBCException) ex).getSQLException());
  191. }
  192. else {
  193. return SessionFactoryUtils.convertHibernateAccessException(ex);
  194. }
  195. }
  196. /**
  197. * Convert the given SQLException to an appropriate exception from the
  198. * org.springframework.dao hierarchy. Uses a JDBC exception translater if set,
  199. * and a generic HibernateJdbcException else. Can be overridden in subclasses.
  200. * <p>Note that SQLException can just occur here when callback code
  201. * performs direct JDBC access via Session.connection().
  202. * @param ex SQLException that occured
  203. * @return the corresponding DataAccessException instance
  204. * @see #setJdbcExceptionTranslator
  205. */
  206. protected DataAccessException convertJdbcAccessException(SQLException ex) {
  207. if (this.jdbcExceptionTranslator != null) {
  208. return this.jdbcExceptionTranslator.translate("HibernateAccessor", null, ex);
  209. }
  210. else {
  211. return new HibernateJdbcException(ex);
  212. }
  213. }
  214. }