001/*
002 * Copyright 2002-2015 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.hibernate4;
018
019import org.hibernate.FlushMode;
020import org.hibernate.HibernateException;
021import org.hibernate.Session;
022import org.hibernate.SessionFactory;
023
024import org.springframework.core.Ordered;
025import org.springframework.dao.DataAccessException;
026import org.springframework.transaction.support.TransactionSynchronization;
027import org.springframework.transaction.support.TransactionSynchronizationManager;
028
029/**
030 * Callback for resource cleanup at the end of a Spring-managed transaction
031 * for a pre-bound Hibernate Session.
032 *
033 * @author Juergen Hoeller
034 * @since 3.1
035 */
036public class SpringSessionSynchronization implements TransactionSynchronization, Ordered {
037
038        private final SessionHolder sessionHolder;
039
040        private final SessionFactory sessionFactory;
041
042        private final boolean newSession;
043
044        private boolean holderActive = true;
045
046
047        public SpringSessionSynchronization(SessionHolder sessionHolder, SessionFactory sessionFactory) {
048                this(sessionHolder, sessionFactory, false);
049        }
050
051        public SpringSessionSynchronization(SessionHolder sessionHolder, SessionFactory sessionFactory, boolean newSession) {
052                this.sessionHolder = sessionHolder;
053                this.sessionFactory = sessionFactory;
054                this.newSession = newSession;
055        }
056
057
058        private Session getCurrentSession() {
059                return this.sessionHolder.getSession();
060        }
061
062
063        @Override
064        public int getOrder() {
065                return SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER;
066        }
067
068        @Override
069        public void suspend() {
070                if (this.holderActive) {
071                        TransactionSynchronizationManager.unbindResource(this.sessionFactory);
072                        // Eagerly disconnect the Session here, to make release mode "on_close" work on JBoss.
073                        getCurrentSession().disconnect();
074                }
075        }
076
077        @Override
078        public void resume() {
079                if (this.holderActive) {
080                        TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
081                }
082        }
083
084        @Override
085        public void flush() {
086                try {
087                        SessionFactoryUtils.logger.debug("Flushing Hibernate Session on explicit request");
088                        getCurrentSession().flush();
089                }
090                catch (HibernateException ex) {
091                        throw SessionFactoryUtils.convertHibernateAccessException(ex);
092                }
093        }
094
095        @Override
096        public void beforeCommit(boolean readOnly) throws DataAccessException {
097                if (!readOnly) {
098                        Session session = getCurrentSession();
099                        // Read-write transaction -> flush the Hibernate Session.
100                        // Further check: only flush when not FlushMode.MANUAL.
101                        if (!session.getFlushMode().equals(FlushMode.MANUAL)) {
102                                try {
103                                        SessionFactoryUtils.logger.debug("Flushing Hibernate Session on transaction synchronization");
104                                        session.flush();
105                                }
106                                catch (HibernateException ex) {
107                                        throw SessionFactoryUtils.convertHibernateAccessException(ex);
108                                }
109                        }
110                }
111        }
112
113        @Override
114        public void beforeCompletion() {
115                try {
116                        Session session = this.sessionHolder.getSession();
117                        if (this.sessionHolder.getPreviousFlushMode() != null) {
118                                // In case of pre-bound Session, restore previous flush mode.
119                                session.setFlushMode(this.sessionHolder.getPreviousFlushMode());
120                        }
121                        // Eagerly disconnect the Session here, to make release mode "on_close" work nicely.
122                        session.disconnect();
123                }
124                finally {
125                        // Unbind at this point if it's a new Session...
126                        if (this.newSession) {
127                                TransactionSynchronizationManager.unbindResource(this.sessionFactory);
128                                this.holderActive = false;
129                        }
130                }
131        }
132
133        @Override
134        public void afterCommit() {
135        }
136
137        @Override
138        public void afterCompletion(int status) {
139                try {
140                        if (status != STATUS_COMMITTED) {
141                                // Clear all pending inserts/updates/deletes in the Session.
142                                // Necessary for pre-bound Sessions, to avoid inconsistent state.
143                                this.sessionHolder.getSession().clear();
144                        }
145                }
146                finally {
147                        this.sessionHolder.setSynchronizedWithTransaction(false);
148                        // Call close() at this point if it's a new Session...
149                        if (this.newSession) {
150                                SessionFactoryUtils.closeSession(this.sessionHolder.getSession());
151                        }
152                }
153        }
154
155}