001/*
002 * Copyright 2002-2016 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.hibernate5;
018
019import javax.transaction.Status;
020import javax.transaction.SystemException;
021import javax.transaction.TransactionManager;
022
023import org.apache.commons.logging.LogFactory;
024import org.hibernate.FlushMode;
025import org.hibernate.HibernateException;
026import org.hibernate.Session;
027import org.hibernate.context.spi.CurrentSessionContext;
028import org.hibernate.engine.spi.SessionFactoryImplementor;
029import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
030
031import org.springframework.transaction.support.TransactionSynchronizationManager;
032
033/**
034 * Implementation of Hibernate 3.1's CurrentSessionContext interface
035 * that delegates to Spring's SessionFactoryUtils for providing a
036 * Spring-managed current Session.
037 *
038 * <p>This CurrentSessionContext implementation can also be specified in custom
039 * SessionFactory setup through the "hibernate.current_session_context_class"
040 * property, with the fully qualified name of this class as value.
041 *
042 * @author Juergen Hoeller
043 * @since 4.2
044 */
045@SuppressWarnings("serial")
046public class SpringSessionContext implements CurrentSessionContext {
047
048        private final SessionFactoryImplementor sessionFactory;
049
050        private TransactionManager transactionManager;
051
052        private CurrentSessionContext jtaSessionContext;
053
054
055        /**
056         * Create a new SpringSessionContext for the given Hibernate SessionFactory.
057         * @param sessionFactory the SessionFactory to provide current Sessions for
058         */
059        public SpringSessionContext(SessionFactoryImplementor sessionFactory) {
060                this.sessionFactory = sessionFactory;
061                try {
062                        JtaPlatform jtaPlatform = sessionFactory.getServiceRegistry().getService(JtaPlatform.class);
063                        this.transactionManager = jtaPlatform.retrieveTransactionManager();
064                        if (this.transactionManager != null) {
065                                this.jtaSessionContext = new SpringJtaSessionContext(sessionFactory);
066                        }
067                }
068                catch (Exception ex) {
069                        LogFactory.getLog(SpringSessionContext.class).warn(
070                                        "Could not introspect Hibernate JtaPlatform for SpringJtaSessionContext", ex);
071                }
072        }
073
074
075        /**
076         * Retrieve the Spring-managed Session for the current thread, if any.
077         */
078        @Override
079        @SuppressWarnings("deprecation")
080        public Session currentSession() throws HibernateException {
081                Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
082                if (value instanceof Session) {
083                        return (Session) value;
084                }
085                else if (value instanceof SessionHolder) {
086                        SessionHolder sessionHolder = (SessionHolder) value;
087                        Session session = sessionHolder.getSession();
088                        if (!sessionHolder.isSynchronizedWithTransaction() &&
089                                        TransactionSynchronizationManager.isSynchronizationActive()) {
090                                TransactionSynchronizationManager.registerSynchronization(
091                                                new SpringSessionSynchronization(sessionHolder, this.sessionFactory, false));
092                                sessionHolder.setSynchronizedWithTransaction(true);
093                                // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
094                                // with FlushMode.MANUAL, which needs to allow flushing within the transaction.
095                                FlushMode flushMode = SessionFactoryUtils.getFlushMode(session);
096                                if (flushMode.equals(FlushMode.MANUAL) &&
097                                                !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
098                                        session.setFlushMode(FlushMode.AUTO);
099                                        sessionHolder.setPreviousFlushMode(flushMode);
100                                }
101                        }
102                        return session;
103                }
104
105                if (this.transactionManager != null) {
106                        try {
107                                if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) {
108                                        Session session = this.jtaSessionContext.currentSession();
109                                        if (TransactionSynchronizationManager.isSynchronizationActive()) {
110                                                TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));
111                                        }
112                                        return session;
113                                }
114                        }
115                        catch (SystemException ex) {
116                                throw new HibernateException("JTA TransactionManager found but status check failed", ex);
117                        }
118                }
119
120                if (TransactionSynchronizationManager.isSynchronizationActive()) {
121                        Session session = this.sessionFactory.openSession();
122                        if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
123                                session.setFlushMode(FlushMode.MANUAL);
124                        }
125                        SessionHolder sessionHolder = new SessionHolder(session);
126                        TransactionSynchronizationManager.registerSynchronization(
127                                        new SpringSessionSynchronization(sessionHolder, this.sessionFactory, true));
128                        TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
129                        sessionHolder.setSynchronizedWithTransaction(true);
130                        return session;
131                }
132                else {
133                        throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");
134                }
135        }
136
137}