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 net.sf.hibernate.FlushMode;
  18. import net.sf.hibernate.HibernateException;
  19. import net.sf.hibernate.Session;
  20. import org.aopalliance.intercept.MethodInterceptor;
  21. import org.aopalliance.intercept.MethodInvocation;
  22. import org.springframework.transaction.support.TransactionSynchronizationManager;
  23. /**
  24. * This interceptor binds a new Hibernate Session to the thread before a method
  25. * call, closing and removing it afterwards in case of any method outcome.
  26. * If there already was a pre-bound Session (e.g. from HibernateTransactionManager,
  27. * or from a surrounding Hibernate-intercepted method), the interceptor simply
  28. * takes part in it.
  29. *
  30. * <p>Application code must retrieve a Hibernate Session via SessionFactoryUtils'
  31. * getSession method, to be able to detect a thread-bound Session. It is preferable
  32. * to use getSession with allowCreate=false, as the code relies on the interceptor
  33. * to provide proper Session handling. Typically the code will look as follows:
  34. *
  35. * <pre>
  36. * public void doHibernateAction() {
  37. * Session session = SessionFactoryUtils.getSession(this.sessionFactory, false);
  38. * try {
  39. * ...
  40. * }
  41. * catch (HibernateException ex) {
  42. * throw SessionFactoryUtils.convertHibernateAccessException(ex);
  43. * }
  44. * }</pre>
  45. *
  46. * Note that the application must care about handling HibernateExceptions itself,
  47. * preferably via delegating to SessionFactoryUtils' convertHibernateAccessException
  48. * that converts them to ones that are compatible with the org.springframework.dao
  49. * exception hierarchy (like HibernateTemplate does).
  50. *
  51. * <p>Unfortunately, this interceptor cannot convert checked HibernateExceptions
  52. * to unchecked dao ones automatically. The intercepted method would have to throw
  53. * HibernateException to be able to achieve this - thus the caller would still have
  54. * to catch or rethrow it, even if it will never be thrown if intercepted.
  55. *
  56. * <p>This class can be considered a declarative alternative to HibernateTemplate's
  57. * callback approach. The advantages are:
  58. * <ul>
  59. * <li>no anonymous classes necessary for callback implementations;
  60. * <li>the possibility to throw any application exceptions from within data access code.
  61. * </ul>
  62. *
  63. * <p>The drawbacks are:
  64. * <ul>
  65. * <li>the dependency on interceptor configuration;
  66. * <li>the delegating try/catch blocks.
  67. * </ul>
  68. *
  69. * <p>Note: Spring's Hibernate support requires Hibernate 2.1 (as of Spring 1.0).
  70. *
  71. * @author Juergen Hoeller
  72. * @since 13.06.2003
  73. * @see SessionFactoryUtils#getSession
  74. * @see HibernateTransactionManager
  75. * @see HibernateTemplate
  76. */
  77. public class HibernateInterceptor extends HibernateAccessor implements MethodInterceptor {
  78. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  79. boolean existingTransaction = false;
  80. Session session = SessionFactoryUtils.getSession(getSessionFactory(), getEntityInterceptor(),
  81. getJdbcExceptionTranslator());
  82. if (TransactionSynchronizationManager.hasResource(getSessionFactory())) {
  83. logger.debug("Found thread-bound session for Hibernate interceptor");
  84. existingTransaction = true;
  85. }
  86. else {
  87. logger.debug("Using new session for Hibernate interceptor");
  88. if (getFlushMode() == FLUSH_NEVER) {
  89. session.setFlushMode(FlushMode.NEVER);
  90. }
  91. TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
  92. }
  93. try {
  94. Object retVal = methodInvocation.proceed();
  95. flushIfNecessary(session, existingTransaction);
  96. return retVal;
  97. }
  98. catch (HibernateException ex) {
  99. throw convertHibernateAccessException(ex);
  100. }
  101. finally {
  102. if (existingTransaction) {
  103. logger.debug("Not closing pre-bound Hibernate session after interceptor");
  104. }
  105. else {
  106. TransactionSynchronizationManager.unbindResource(getSessionFactory());
  107. SessionFactoryUtils.closeSessionIfNecessary(session, getSessionFactory());
  108. }
  109. }
  110. }
  111. }