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