001/*
002 * Copyright 2002-2014 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.orm.hibernate3;
018
019import org.aopalliance.intercept.MethodInterceptor;
020import org.aopalliance.intercept.MethodInvocation;
021import org.hibernate.FlushMode;
022import org.hibernate.HibernateException;
023import org.hibernate.Session;
024
025import org.springframework.transaction.support.TransactionSynchronizationManager;
026
027/**
028 * This interceptor binds a new Hibernate Session to the thread before a method
029 * call, closing and removing it afterwards in case of any method outcome.
030 * If there already is a pre-bound Session (e.g. from HibernateTransactionManager,
031 * or from a surrounding Hibernate-intercepted method), the interceptor simply
032 * participates in it.
033 *
034 * <p>Application code must retrieve a Hibernate Session via the
035 * {@code SessionFactoryUtils.getSession} method or - preferably -
036 * Hibernate's own {@code SessionFactory.getCurrentSession()} method, to be
037 * able to detect a thread-bound Session. Typically, the code will look like as follows:
038 *
039 * <pre class="code">
040 * public void doSomeDataAccessAction() {
041 *   Session session = this.sessionFactory.getCurrentSession();
042 *   ...
043 *   // No need to close the Session or translate exceptions!
044 * }</pre>
045 *
046 * Note that this interceptor automatically translates HibernateExceptions,
047 * via delegating to the {@code SessionFactoryUtils.convertHibernateAccessException}
048 * method that converts them to exceptions that are compatible with the
049 * {@code org.springframework.dao} exception hierarchy (like HibernateTemplate does).
050 * This can be turned off if the raw exceptions are preferred.
051 *
052 * <p>This class can be considered a declarative alternative to HibernateTemplate's
053 * callback approach. The advantages are:
054 * <ul>
055 * <li>no anonymous classes necessary for callback implementations;
056 * <li>the possibility to throw any application exceptions from within data access code.
057 * </ul>
058 *
059 * <p>The drawback is the dependency on interceptor configuration. However, note
060 * that this interceptor is usually <i>not</i> necessary in scenarios where the
061 * data access code always executes within transactions. A transaction will always
062 * have a thread-bound Session in the first place, so adding this interceptor to the
063 * configuration just adds value when fine-tuning Session settings like the flush mode
064 * - or when relying on exception translation.
065 *
066 * @author Juergen Hoeller
067 * @since 1.2
068 * @see org.hibernate.SessionFactory#getCurrentSession()
069 * @see HibernateTransactionManager
070 * @see HibernateTemplate
071 * @deprecated as of Spring 3.2.7, in favor of either HibernateTemplate usage or
072 * native Hibernate API usage within transactions, in combination with a general
073 * {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor}.
074 * Note: This class does not have an equivalent replacement in {@code orm.hibernate4}.
075 * If you desperately need a scoped Session bound through AOP, consider the newly
076 * introduced {@link org.springframework.orm.hibernate3.support.OpenSessionInterceptor}.
077 */
078@Deprecated
079public class HibernateInterceptor extends HibernateAccessor implements MethodInterceptor {
080
081        private boolean exceptionConversionEnabled = true;
082
083
084        /**
085         * Set whether to convert any HibernateException raised to a Spring DataAccessException,
086         * compatible with the {@code org.springframework.dao} exception hierarchy.
087         * <p>Default is "true". Turn this flag off to let the caller receive raw exceptions
088         * as-is, without any wrapping.
089         * @see org.springframework.dao.DataAccessException
090         */
091        public void setExceptionConversionEnabled(boolean exceptionConversionEnabled) {
092                this.exceptionConversionEnabled = exceptionConversionEnabled;
093        }
094
095
096        @Override
097        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
098                Session session = getSession();
099                SessionHolder sessionHolder =
100                                (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
101
102                boolean existingTransaction = (sessionHolder != null && sessionHolder.containsSession(session));
103                if (existingTransaction) {
104                        logger.debug("Found thread-bound Session for HibernateInterceptor");
105                }
106                else {
107                        if (sessionHolder != null) {
108                                sessionHolder.addSession(session);
109                        }
110                        else {
111                                TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
112                        }
113                }
114
115                FlushMode previousFlushMode = null;
116                try {
117                        previousFlushMode = applyFlushMode(session, existingTransaction);
118                        enableFilters(session);
119                        Object retVal = methodInvocation.proceed();
120                        flushIfNecessary(session, existingTransaction);
121                        return retVal;
122                }
123                catch (HibernateException ex) {
124                        if (this.exceptionConversionEnabled) {
125                                throw convertHibernateAccessException(ex);
126                        }
127                        else {
128                                throw ex;
129                        }
130                }
131                finally {
132                        if (existingTransaction) {
133                                logger.debug("Not closing pre-bound Hibernate Session after HibernateInterceptor");
134                                disableFilters(session);
135                                if (previousFlushMode != null) {
136                                        session.setFlushMode(previousFlushMode);
137                                }
138                        }
139                        else {
140                                SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
141                                if (sessionHolder == null || sessionHolder.doesNotHoldNonDefaultSession()) {
142                                        TransactionSynchronizationManager.unbindResource(getSessionFactory());
143                                }
144                        }
145                }
146        }
147
148        /**
149         * Return a Session for use by this interceptor.
150         * @see SessionFactoryUtils#getSession
151         */
152        protected Session getSession() {
153                return SessionFactoryUtils.getSession(
154                                getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
155        }
156
157}