SpringSessionContext for Hibernate 4 supports lazily bound Session for propagation SUPPORTS
Issue: SPR-9020
This commit is contained in:
parent
562916b69c
commit
5cbb1fc498
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -102,7 +102,9 @@ public abstract class SessionFactoryUtils {
|
|||
public static DataSource getDataSource(SessionFactory sessionFactory) {
|
||||
if (getConnectionProviderMethod != null && sessionFactory instanceof SessionFactoryImplementor) {
|
||||
Wrapped cp = (Wrapped) ReflectionUtils.invokeMethod(getConnectionProviderMethod, sessionFactory);
|
||||
return cp.unwrap(DataSource.class);
|
||||
if (cp != null) {
|
||||
return cp.unwrap(DataSource.class);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -17,7 +17,11 @@
|
|||
package org.springframework.orm.hibernate4;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import javax.transaction.Status;
|
||||
import javax.transaction.SystemException;
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
|
|
@ -44,6 +48,8 @@ public class SpringSessionContext implements CurrentSessionContext {
|
|||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
private TransactionManager transactionManager;
|
||||
|
||||
private CurrentSessionContext jtaSessionContext;
|
||||
|
||||
|
||||
|
|
@ -56,13 +62,14 @@ public class SpringSessionContext implements CurrentSessionContext {
|
|||
try {
|
||||
Object jtaPlatform = sessionFactory.getServiceRegistry().getService(ConfigurableJtaPlatform.jtaPlatformClass);
|
||||
Method rtmMethod = ConfigurableJtaPlatform.jtaPlatformClass.getMethod("retrieveTransactionManager");
|
||||
Object transactionManager = ReflectionUtils.invokeMethod(rtmMethod, jtaPlatform);
|
||||
if (transactionManager != null) {
|
||||
this.transactionManager = (TransactionManager) ReflectionUtils.invokeMethod(rtmMethod, jtaPlatform);
|
||||
if (this.transactionManager != null) {
|
||||
this.jtaSessionContext = new SpringJtaSessionContext(sessionFactory);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Could not introspect Hibernate JtaPlatform for SpringJtaSessionContext", ex);
|
||||
LogFactory.getLog(SpringSessionContext.class).warn(
|
||||
"Could not introspect Hibernate JtaPlatform for SpringJtaSessionContext", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +89,7 @@ public class SpringSessionContext implements CurrentSessionContext {
|
|||
if (TransactionSynchronizationManager.isSynchronizationActive() &&
|
||||
!sessionHolder.isSynchronizedWithTransaction()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
new SpringSessionSynchronization(sessionHolder, this.sessionFactory));
|
||||
new SpringSessionSynchronization(sessionHolder, this.sessionFactory, false));
|
||||
sessionHolder.setSynchronizedWithTransaction(true);
|
||||
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
|
||||
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
|
||||
|
|
@ -95,15 +102,36 @@ public class SpringSessionContext implements CurrentSessionContext {
|
|||
}
|
||||
return session;
|
||||
}
|
||||
else if (this.jtaSessionContext != null) {
|
||||
Session session = this.jtaSessionContext.currentSession();
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));
|
||||
|
||||
if (this.transactionManager != null) {
|
||||
try {
|
||||
if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) {
|
||||
Session session = this.jtaSessionContext.currentSession();
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));
|
||||
}
|
||||
return session;
|
||||
}
|
||||
}
|
||||
catch (SystemException ex) {
|
||||
throw new HibernateException("JTA TransactionManager found but status check failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
Session session = this.sessionFactory.openSession();
|
||||
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
|
||||
session.setFlushMode(FlushMode.MANUAL);
|
||||
}
|
||||
SessionHolder sessionHolder = new SessionHolder(session);
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
new SpringSessionSynchronization(sessionHolder, this.sessionFactory, true));
|
||||
TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
|
||||
sessionHolder.setSynchronizedWithTransaction(true);
|
||||
return session;
|
||||
}
|
||||
else {
|
||||
throw new HibernateException("No Session found for current thread");
|
||||
throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -39,12 +39,15 @@ class SpringSessionSynchronization implements TransactionSynchronization, Ordere
|
|||
|
||||
private final SessionFactory sessionFactory;
|
||||
|
||||
private final boolean newSession;
|
||||
|
||||
private boolean holderActive = true;
|
||||
|
||||
|
||||
public SpringSessionSynchronization(SessionHolder sessionHolder, SessionFactory sessionFactory) {
|
||||
public SpringSessionSynchronization(SessionHolder sessionHolder, SessionFactory sessionFactory, boolean newSession) {
|
||||
this.sessionHolder = sessionHolder;
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.newSession = newSession;
|
||||
}
|
||||
|
||||
private Session getCurrentSession() {
|
||||
|
|
@ -111,6 +114,12 @@ class SpringSessionSynchronization implements TransactionSynchronization, Ordere
|
|||
}
|
||||
// Eagerly disconnect the Session here, to make release mode "on_close" work nicely.
|
||||
session.disconnect();
|
||||
|
||||
// Unbind at this point if it's a new Session...
|
||||
if (this.newSession) {
|
||||
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
|
||||
this.holderActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -128,6 +137,10 @@ class SpringSessionSynchronization implements TransactionSynchronization, Ordere
|
|||
}
|
||||
finally {
|
||||
this.sessionHolder.setSynchronizedWithTransaction(false);
|
||||
// Call close() at this point if it's a new Session...
|
||||
if (this.newSession) {
|
||||
SessionFactoryUtils.closeSession(this.sessionHolder.getSession());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import java.sql.Savepoint;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import javax.transaction.TransactionManager;
|
||||
import javax.transaction.TransactionSynchronizationRegistry;
|
||||
|
|
@ -38,13 +37,12 @@ import org.hibernate.SessionFactory;
|
|||
import org.hibernate.Transaction;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.HSQLDialect;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
|
@ -495,6 +493,49 @@ public class HibernateTransactionManagerTests {
|
|||
ordered.verify(session).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithPropagationSupportsAndCurrentSession() throws Exception {
|
||||
final SessionFactoryImplementor sf = mock(SessionFactoryImplementor.class);
|
||||
final Session session = mock(Session.class);
|
||||
|
||||
given(sf.openSession()).willReturn(session);
|
||||
given(session.getSessionFactory()).willReturn(sf);
|
||||
given(session.getFlushMode()).willReturn(FlushMode.MANUAL);
|
||||
|
||||
LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() {
|
||||
@Override
|
||||
protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {
|
||||
return sf;
|
||||
}
|
||||
};
|
||||
lsfb.afterPropertiesSet();
|
||||
final SessionFactory sfProxy = lsfb.getObject();
|
||||
|
||||
PlatformTransactionManager tm = new HibernateTransactionManager(sfProxy);
|
||||
TransactionTemplate tt = new TransactionTemplate(tm);
|
||||
tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
|
||||
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy));
|
||||
|
||||
tt.execute(new TransactionCallback() {
|
||||
@Override
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy));
|
||||
assertTrue("Is not new transaction", !status.isNewTransaction());
|
||||
assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
|
||||
assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
|
||||
Session session = new SpringSessionContext(sf).currentSession();
|
||||
assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sfProxy));
|
||||
session.flush();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy));
|
||||
InOrder ordered = inOrder(session);
|
||||
ordered.verify(session).flush();
|
||||
ordered.verify(session).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithPropagationSupportsAndInnerTransaction() throws Exception {
|
||||
final SessionFactory sf = mock(SessionFactory.class);
|
||||
|
|
@ -735,7 +776,7 @@ public class HibernateTransactionManagerTests {
|
|||
catch (DataIntegrityViolationException ex) {
|
||||
// expected
|
||||
assertEquals(rootCause, ex.getCause());
|
||||
assertTrue(ex.getMessage().indexOf("mymsg") != -1);
|
||||
assertTrue(ex.getMessage().contains("mymsg"));
|
||||
}
|
||||
|
||||
assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
|
||||
|
|
|
|||
Loading…
Reference in New Issue