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