1 /* 2 * Copyright 2002-2007 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 17 package org.springframework.orm.hibernate3; 18 19 import org.aopalliance.intercept.MethodInterceptor; 20 import org.aopalliance.intercept.MethodInvocation; 21 import org.hibernate.FlushMode; 22 import org.hibernate.HibernateException; 23 import org.hibernate.Session; 24 25 import org.springframework.transaction.support.TransactionSynchronizationManager; 26 27 /** 28 * This interceptor binds a new Hibernate Session to the thread before a method 29 * call, closing and removing it afterwards in case of any method outcome. 30 * If there already is a pre-bound Session (e.g. from HibernateTransactionManager, 31 * or from a surrounding Hibernate-intercepted method), the interceptor simply 32 * participates in it. 33 * 34 * <p>Application code must retrieve a Hibernate Session via the 35 * <code>SessionFactoryUtils.getSession</code> method or - preferably - 36 * Hibernate's own <code>SessionFactory.getCurrentSession()</code> method, to be 37 * able to detect a thread-bound Session. Typically, the code will look like as follows: 38 * 39 * <pre> 40 * public void doSomeDataAccessAction() { 41 * Session session = this.sessionFactory.getCurrentSession(); 42 * ... 43 * // No need to close the Session or translate exceptions! 44 * }</pre> 45 * 46 * Note that this interceptor automatically translates HibernateExceptions, 47 * via delegating to the <code>SessionFactoryUtils.convertHibernateAccessException</code> 48 * method that converts them to exceptions that are compatible with the 49 * <code>org.springframework.dao</code> exception hierarchy (like HibernateTemplate does). 50 * This can be turned off if the raw exceptions are preferred. 51 * 52 * <p>This class can be considered a declarative alternative to HibernateTemplate's 53 * callback approach. The advantages are: 54 * <ul> 55 * <li>no anonymous classes necessary for callback implementations; 56 * <li>the possibility to throw any application exceptions from within data access code. 57 * </ul> 58 * 59 * <p>The drawback is the dependency on interceptor configuration. However, note 60 * that this interceptor is usually <i>not</i> necessary in scenarios where the 61 * data access code always executes within transactions. A transaction will always 62 * have a thread-bound Session in the first place, so adding this interceptor to the 63 * configuration just adds value when fine-tuning Session settings like the flush mode 64 * - or when relying on exception translation. 65 * 66 * @author Juergen Hoeller 67 * @since 1.2 68 * @see org.hibernate.SessionFactory#getCurrentSession() 69 * @see HibernateTransactionManager 70 * @see HibernateTemplate 71 */ 72 public class HibernateInterceptor extends HibernateAccessor implements MethodInterceptor { 73 74 private boolean exceptionConversionEnabled = true; 75 76 77 /** 78 * Set whether to convert any HibernateException raised to a Spring DataAccessException, 79 * compatible with the <code>org.springframework.dao</code> exception hierarchy. 80 * <p>Default is "true". Turn this flag off to let the caller receive raw exceptions 81 * as-is, without any wrapping. 82 * @see org.springframework.dao.DataAccessException 83 */ setExceptionConversionEnabled(boolean exceptionConversionEnabled)84 public void setExceptionConversionEnabled(boolean exceptionConversionEnabled) { 85 this.exceptionConversionEnabled = exceptionConversionEnabled; 86 } 87 88 invoke(MethodInvocation methodInvocation)89 public Object invoke(MethodInvocation methodInvocation) throws Throwable { 90 Session session = getSession(); 91 SessionHolder sessionHolder = 92 (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory()); 93 94 boolean existingTransaction = (sessionHolder != null && sessionHolder.containsSession(session)); 95 if (existingTransaction) { 96 logger.debug("Found thread-bound Session for HibernateInterceptor"); 97 } 98 else { 99 if (sessionHolder != null) { 100 sessionHolder.addSession(session); 101 } 102 else { 103 TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session)); 104 } 105 } 106 107 FlushMode previousFlushMode = null; 108 try { 109 previousFlushMode = applyFlushMode(session, existingTransaction); 110 enableFilters(session); 111 Object retVal = methodInvocation.proceed(); 112 flushIfNecessary(session, existingTransaction); 113 return retVal; 114 } 115 catch (HibernateException ex) { 116 if (this.exceptionConversionEnabled) { 117 throw convertHibernateAccessException(ex); 118 } 119 else { 120 throw ex; 121 } 122 } 123 finally { 124 if (existingTransaction) { 125 logger.debug("Not closing pre-bound Hibernate Session after HibernateInterceptor"); 126 disableFilters(session); 127 if (previousFlushMode != null) { 128 session.setFlushMode(previousFlushMode); 129 } 130 } 131 else { 132 SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory()); 133 if (sessionHolder == null || sessionHolder.doesNotHoldNonDefaultSession()) { 134 TransactionSynchronizationManager.unbindResource(getSessionFactory()); 135 } 136 } 137 } 138 } 139 140 /** 141 * Return a Session for use by this interceptor. 142 * @see SessionFactoryUtils#getSession 143 */ getSession()144 protected Session getSession() { 145 return SessionFactoryUtils.getSession( 146 getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator()); 147 } 148 149 } 150