From 096972d2b10d5a3df38c855f63a4e4340b1df136 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 27 Feb 2013 00:22:18 +0100 Subject: [PATCH] HibernateTransactionManager for Hibernate 4 supports "entityInterceptor(BeanName)" property Issue: SPR-10301 --- .../HibernateTransactionManager.java | 92 +- .../HibernateTransactionManagerTests.java | 1006 +++++++---------- 2 files changed, 470 insertions(+), 628 deletions(-) diff --git a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateTransactionManager.java b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateTransactionManager.java index 6d3e7ccf256..5871bb8a87e 100644 --- a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateTransactionManager.java +++ b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateTransactionManager.java @@ -22,12 +22,16 @@ import javax.sql.DataSource; import org.hibernate.ConnectionReleaseMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; +import org.hibernate.Interceptor; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.transaction.spi.TransactionContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; @@ -100,7 +104,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager */ @SuppressWarnings("serial") public class HibernateTransactionManager extends AbstractPlatformTransactionManager - implements ResourceTransactionManager, InitializingBean { + implements ResourceTransactionManager, BeanFactoryAware, InitializingBean { private SessionFactory sessionFactory; @@ -112,6 +116,14 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana private boolean hibernateManagedSession = false; + private Object entityInterceptor; + + /** + * Just needed for entityInterceptorBeanName. + * @see #setEntityInterceptorBeanName + */ + private BeanFactory beanFactory; + /** * Create a new HibernateTransactionManager instance. @@ -229,7 +241,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana * to always return a proper Session when called for a Spring-managed transaction; * transaction begin will fail if the {@code getCurrentSession()} call fails. *

This mode will typically be used in combination with a custom Hibernate - * {@link org.hibernate.context.CurrentSessionContext} implementation that stores + * {@link org.hibernate.context.spi.CurrentSessionContext} implementation that stores * Sessions in a place other than Spring's TransactionSynchronizationManager. * It may also be used in combination with Spring's Open-Session-in-View support * (using Spring's default {@link SpringSessionContext}), in which case it subtly @@ -242,10 +254,81 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana this.hibernateManagedSession = hibernateManagedSession; } + /** + * Set the bean name of a Hibernate entity interceptor that allows to inspect + * and change property values before writing to and reading from the database. + * Will get applied to any new Session created by this transaction manager. + *

Requires the bean factory to be known, to be able to resolve the bean + * name to an interceptor instance on session creation. Typically used for + * prototype interceptors, i.e. a new interceptor instance per session. + *

Can also be used for shared interceptor instances, but it is recommended + * to set the interceptor reference directly in such a scenario. + * @param entityInterceptorBeanName the name of the entity interceptor in + * the bean factory + * @see #setBeanFactory + * @see #setEntityInterceptor + */ + public void setEntityInterceptorBeanName(String entityInterceptorBeanName) { + this.entityInterceptor = entityInterceptorBeanName; + } + + /** + * Set a Hibernate entity interceptor that allows to inspect and change + * property values before writing to and reading from the database. + * Will get applied to any new Session created by this transaction manager. + *

Such an interceptor can either be set at the SessionFactory level, + * i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on + * HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + * It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager + * to avoid repeated configuration and guarantee consistent behavior in transactions. + * @see LocalSessionFactoryBean#setEntityInterceptor + */ + public void setEntityInterceptor(Interceptor entityInterceptor) { + this.entityInterceptor = entityInterceptor; + } + + /** + * Return the current Hibernate entity interceptor, or {@code null} if none. + * Resolves an entity interceptor bean name via the bean factory, + * if necessary. + * @throws IllegalStateException if bean name specified but no bean factory set + * @throws BeansException if bean name resolution via the bean factory failed + * @see #setEntityInterceptor + * @see #setEntityInterceptorBeanName + * @see #setBeanFactory + */ + public Interceptor getEntityInterceptor() throws IllegalStateException, BeansException { + if (this.entityInterceptor instanceof Interceptor) { + return (Interceptor) entityInterceptor; + } + else if (this.entityInterceptor instanceof String) { + if (this.beanFactory == null) { + throw new IllegalStateException("Cannot get entity interceptor via bean name if no bean factory set"); + } + String beanName = (String) this.entityInterceptor; + return this.beanFactory.getBean(beanName, Interceptor.class); + } + else { + return null; + } + } + + /** + * The bean factory just needs to be known for resolving entity interceptor + * bean names. It does not need to be set for any other mode of operation. + * @see #setEntityInterceptorBeanName + */ + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + public void afterPropertiesSet() { if (getSessionFactory() == null) { throw new IllegalArgumentException("Property 'sessionFactory' is required"); } + if (this.entityInterceptor instanceof String && this.beanFactory == null) { + throw new IllegalArgumentException("Property 'beanFactory' is required for 'entityInterceptorBeanName'"); + } // Check for SessionFactory's DataSource. if (this.autodetectDataSource && getDataSource() == null) { @@ -325,7 +408,10 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana try { if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) { - Session newSession = getSessionFactory().openSession(); + Interceptor entityInterceptor = getEntityInterceptor(); + Session newSession = (entityInterceptor != null ? + getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() : + getSessionFactory().openSession()); if (logger.isDebugEnabled()) { logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction"); } diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/orm/hibernate4/HibernateTransactionManagerTests.java b/spring-orm-hibernate4/src/test/java/org/springframework/orm/hibernate4/HibernateTransactionManagerTests.java index 26bdd97e292..f235fc196e0 100644 --- a/spring-orm-hibernate4/src/test/java/org/springframework/orm/hibernate4/HibernateTransactionManagerTests.java +++ b/spring-orm-hibernate4/src/test/java/org/springframework/orm/hibernate4/HibernateTransactionManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -25,17 +25,21 @@ import java.util.List; import java.util.Properties; import javax.sql.DataSource; -import junit.framework.TestCase; -import org.easymock.MockControl; import org.hibernate.FlushMode; +import org.hibernate.Interceptor; import org.hibernate.Query; import org.hibernate.Session; +import org.hibernate.SessionBuilder; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.dialect.HSQLDialect; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.exception.ConstraintViolationException; +import org.junit.After; +import org.junit.Test; +import org.mockito.InOrder; +import org.springframework.beans.factory.BeanFactory; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.jdbc.datasource.ConnectionHolder; import org.springframework.jdbc.datasource.DriverManagerDataSource; @@ -50,63 +54,42 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; +import static org.junit.Assert.*; +import static org.mockito.BDDMockito.*; + /** * @author Juergen Hoeller * @since 3.2 */ -public class HibernateTransactionManagerTests extends TestCase { +public class HibernateTransactionManagerTests { + @After + public void tearDown() { + assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty()); + assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertFalse(TransactionSynchronizationManager.isActualTransactionActive()); + } + + @Test public void testTransactionCommit() throws Exception { - MockControl dsControl = MockControl.createControl(DataSource.class); - final DataSource ds = (DataSource) dsControl.getMock(); - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); - MockControl queryControl = MockControl.createControl(Query.class); - Query query = (Query) queryControl.getMock(); + final DataSource ds = mock(DataSource.class); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + final ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); + Query query = mock(Query.class); final List list = new ArrayList(); list.add("test"); - con.getTransactionIsolation(); - conControl.setReturnValue(Connection.TRANSACTION_READ_COMMITTED); - con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); - conControl.setVoidCallable(1); - con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); - conControl.setVoidCallable(1); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.getTransaction(); - sessionControl.setReturnValue(tx, 1); - tx.setTimeout(10); - txControl.setVoidCallable(1); - tx.begin(); - txControl.setVoidCallable(1); - session.connection(); - sessionControl.setReturnValue(con, 3); - session.createQuery("some query string"); - sessionControl.setReturnValue(query, 1); - query.list(); - queryControl.setReturnValue(list, 1); - tx.commit(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - session.close(); - sessionControl.setReturnValue(null, 1); - - dsControl.replay(); - conControl.replay(); - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); - queryControl.replay(); + given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_COMMITTED); + given(sf.openSession()).willReturn(session); + given(session.getTransaction()).willReturn(tx); + given(session.connection()).willReturn(con); + given(session.isOpen()).willReturn(true); + given(session.createQuery("some query string")).willReturn(query); + given(query.list()).willReturn(list); + given(session.isConnected()).willReturn(true); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { @Override @@ -143,42 +126,27 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy)); assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); - dsControl.verify(); - conControl.verify(); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); - queryControl.verify(); + + verify(con).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); + verify(con).setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + verify(tx).setTimeout(10); + verify(tx).begin(); + verify(tx).commit(); + verify(session).close(); } + @Test public void testTransactionRollback() throws Exception { - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.close(); - sessionControl.setReturnValue(null, 1); - tx.rollback(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - session.connection(); - sessionControl.setReturnValue(con, 2); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); PlatformTransactionManager tm = new HibernateTransactionManager(sf); TransactionTemplate tt = new TransactionTemplate(tm); @@ -201,40 +169,23 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); + verify(session).close(); + verify(tx).rollback(); } + @Test public void testTransactionRollbackOnly() throws Exception { - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.flush(); - sessionControl.setVoidCallable(1); - session.close(); - sessionControl.setReturnValue(null, 1); - tx.rollback(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - session.connection(); - sessionControl.setReturnValue(con, 2); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); + given(session.getFlushMode()).willReturn(FlushMode.AUTO); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); PlatformTransactionManager tm = new HibernateTransactionManager(sf); TransactionTemplate tt = new TransactionTemplate(tm); @@ -252,40 +203,24 @@ public class HibernateTransactionManagerTests extends TestCase { }); assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); + verify(session).flush(); + verify(session).close(); + verify(tx).rollback(); } + @Test public void testParticipatingTransactionWithCommit() throws Exception { - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + final ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.flush(); - sessionControl.setVoidCallable(1); - session.close(); - sessionControl.setReturnValue(null, 1); - tx.commit(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - session.connection(); - sessionControl.setReturnValue(con, 2); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); + given(session.getFlushMode()).willReturn(FlushMode.AUTO); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { @Override @@ -316,38 +251,24 @@ public class HibernateTransactionManagerTests extends TestCase { }); assertTrue("Correct result list", result == l); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); + verify(session).flush(); + verify(session).close(); + verify(tx).commit(); } + @Test public void testParticipatingTransactionWithRollback() throws Exception { - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.close(); - sessionControl.setReturnValue(null, 1); - tx.rollback(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - session.connection(); - sessionControl.setReturnValue(con, 2); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); + given(session.getFlushMode()).willReturn(FlushMode.AUTO); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); PlatformTransactionManager tm = new HibernateTransactionManager(sf); final TransactionTemplate tt = new TransactionTemplate(tm); @@ -369,41 +290,27 @@ public class HibernateTransactionManagerTests extends TestCase { // expected } - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); + verify(session).close(); + verify(tx).rollback(); } + @Test public void testParticipatingTransactionWithRollbackOnly() throws Exception { - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.close(); - sessionControl.setReturnValue(null, 1); - tx.rollback(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - session.connection(); - sessionControl.setReturnValue(con, 2); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); PlatformTransactionManager tm = new HibernateTransactionManager(sf); final TransactionTemplate tt = new TransactionTemplate(tm); + final List l = new ArrayList(); + l.add("test"); try { tt.execute(new TransactionCallback() { @@ -424,55 +331,28 @@ public class HibernateTransactionManagerTests extends TestCase { // expected } - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); + verify(session).close(); + verify(tx).rollback(); } - public void testParticipatingTransactionWithWithRequiresNew() throws Exception { - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl session1Control = MockControl.createControl(ImplementingSession.class); - ImplementingSession session1 = (ImplementingSession) session1Control.getMock(); - MockControl session2Control = MockControl.createControl(ImplementingSession.class); - ImplementingSession session2 = (ImplementingSession) session2Control.getMock(); - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + @Test + public void testParticipatingTransactionWithRequiresNew() throws Exception { + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session1 = mock(ImplementingSession.class); + ImplementingSession session2 = mock(ImplementingSession.class); + Connection con = mock(Connection.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session1, 1); - sf.openSession(); - sfControl.setReturnValue(session2, 1); - session1.beginTransaction(); - session1Control.setReturnValue(tx, 1); - session2.beginTransaction(); - session2Control.setReturnValue(tx, 1); - session2.flush(); - session2Control.setVoidCallable(1); - session1.close(); - session1Control.setReturnValue(null, 1); - session2.close(); - session2Control.setReturnValue(null, 1); - tx.commit(); - txControl.setVoidCallable(2); - session1.isConnected(); - session1Control.setReturnValue(true, 1); - session1.connection(); - session1Control.setReturnValue(con, 2); - session2.isConnected(); - session2Control.setReturnValue(true, 1); - session2.connection(); - session2Control.setReturnValue(con, 2); - con.isReadOnly(); - conControl.setReturnValue(false, 2); - - sfControl.replay(); - session1Control.replay(); - session2Control.replay(); - conControl.replay(); - txControl.replay(); + given(sf.openSession()).willReturn(session1, session2); + given(session1.beginTransaction()).willReturn(tx); + given(session1.isOpen()).willReturn(true); + given(session2.beginTransaction()).willReturn(tx); + given(session2.isOpen()).willReturn(true); + given(session2.getFlushMode()).willReturn(FlushMode.AUTO); + given(session1.isConnected()).willReturn(true); + given(session1.connection()).willReturn(con); + given(session2.isConnected()).willReturn(true); + given(session2.connection()).willReturn(con); PlatformTransactionManager tm = new HibernateTransactionManager(sf); final TransactionTemplate tt = new TransactionTemplate(tm); @@ -506,42 +386,26 @@ public class HibernateTransactionManagerTests extends TestCase { }); assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); - sfControl.verify(); - session1Control.verify(); - session2Control.verify(); - conControl.verify(); - txControl.verify(); + verify(session2).flush(); + verify(session1).close(); + verify(session2).close(); + verify(tx, times(2)).commit(); } - public void testParticipatingTransactionWithWithNotSupported() throws Exception { - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + @Test + public void testParticipatingTransactionWithNotSupported() throws Exception { + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + Connection con = mock(Connection.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.close(); - sessionControl.setReturnValue(null, 1); - tx.commit(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - session.connection(); - sessionControl.setReturnValue(con, 2); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - - sfControl.replay(); - sessionControl.replay(); - conControl.replay(); - txControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.getSessionFactory()).willReturn(sf); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); + given(session.getFlushMode()).willReturn(FlushMode.AUTO); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); HibernateTransactionManager tm = new HibernateTransactionManager(sf); final TransactionTemplate tt = new TransactionTemplate(tm); @@ -574,25 +438,18 @@ public class HibernateTransactionManagerTests extends TestCase { }); assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); + verify(session).close(); + verify(tx).commit(); } + @Test public void testTransactionWithPropagationSupports() throws Exception { - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + final SessionFactory sf = mock(SessionFactory.class); + final Session session = mock(Session.class); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.flush(); - sessionControl.setVoidCallable(1); - session.close(); - sessionControl.setReturnValue(null, 1); - sfControl.replay(); - sessionControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.getSessionFactory()).willReturn(sf); + given(session.getFlushMode()).willReturn(FlushMode.MANUAL); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { @Override @@ -623,47 +480,27 @@ public class HibernateTransactionManagerTests extends TestCase { }); assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy)); - sfControl.verify(); - sessionControl.verify(); + InOrder ordered = inOrder(session); + ordered.verify(session).flush(); + ordered.verify(session).close(); } + @Test public void testTransactionWithPropagationSupportsAndInnerTransaction() throws Exception { - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl session1Control = MockControl.createControl(ImplementingSession.class); - final ImplementingSession session1 = (ImplementingSession) session1Control.getMock(); - MockControl session2Control = MockControl.createControl(ImplementingSession.class); - final ImplementingSession session2 = (ImplementingSession) session2Control.getMock(); - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + final SessionFactory sf = mock(SessionFactory.class); + final ImplementingSession session1 = mock(ImplementingSession.class); + final ImplementingSession session2 = mock(ImplementingSession.class); + Connection con = mock(Connection.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session1, 1); - session1.flush(); - session1Control.setVoidCallable(1); - session1.close(); - session1Control.setReturnValue(null, 1); - - sf.openSession(); - sfControl.setReturnValue(session2, 1); - session2.beginTransaction(); - session2Control.setReturnValue(tx, 1); - session2.connection(); - session2Control.setReturnValue(con, 2); - session2.flush(); - session2Control.setVoidCallable(1); - tx.commit(); - txControl.setVoidCallable(1); - session2.isConnected(); - session2Control.setReturnValue(true, 1); - session2.close(); - session2Control.setReturnValue(null, 1); - sfControl.replay(); - session1Control.replay(); - session2Control.replay(); - txControl.replay(); + given(sf.openSession()).willReturn(session1, session2); + given(session1.getSessionFactory()).willReturn(sf); + given(session1.getFlushMode()).willReturn(FlushMode.AUTO); + given(session2.beginTransaction()).willReturn(tx); + given(session2.connection()).willReturn(con); + given(session2.getFlushMode()).willReturn(FlushMode.AUTO); + given(session2.isOpen()).willReturn(true); + given(session2.isConnected()).willReturn(true); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { @Override @@ -710,56 +547,117 @@ public class HibernateTransactionManagerTests extends TestCase { }); assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); - sfControl.verify(); - session1Control.verify(); - session2Control.verify(); - txControl.verify(); + verify(session1).flush(); + verify(session1).close(); + verify(session2).flush(); + verify(session2).close(); + verify(tx).commit(); } + @Test + public void testTransactionCommitWithEntityInterceptor() throws Exception { + Interceptor entityInterceptor = mock(Interceptor.class); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + SessionBuilder options = mock(SessionBuilder.class); + Transaction tx = mock(Transaction.class); + + given(sf.withOptions()).willReturn(options); + given(options.interceptor(entityInterceptor)).willReturn(options); + given(options.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); + + HibernateTransactionManager tm = new HibernateTransactionManager(sf); + tm.setEntityInterceptor(entityInterceptor); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + Object result = tt.execute(new TransactionCallbackWithoutResult() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + } + }); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + verify(session).close(); + verify(tx).commit(); + } + + @Test + public void testTransactionCommitWithEntityInterceptorBeanName() throws Exception { + Interceptor entityInterceptor = mock(Interceptor.class); + Interceptor entityInterceptor2 = mock(Interceptor.class); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + SessionBuilder options = mock(SessionBuilder.class); + Transaction tx = mock(Transaction.class); + + given(sf.withOptions()).willReturn(options); + given(options.interceptor(entityInterceptor)).willReturn(options); + given(options.interceptor(entityInterceptor2)).willReturn(options); + given(options.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); + + BeanFactory beanFactory = mock(BeanFactory.class); + given(beanFactory.getBean("entityInterceptor", Interceptor.class)).willReturn( + entityInterceptor, entityInterceptor2); + + HibernateTransactionManager tm = new HibernateTransactionManager(sf); + tm.setEntityInterceptorBeanName("entityInterceptor"); + tm.setBeanFactory(beanFactory); + + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + for (int i = 0; i < 2; i++) { + tt.execute(new TransactionCallbackWithoutResult() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + } + }); + } + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + verify(session, times(2)).close(); + verify(tx, times(2)).commit(); + } + + @Test public void testTransactionCommitWithReadOnly() throws Exception { - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); - MockControl queryControl = MockControl.createControl(Query.class); - Query query = (Query) queryControl.getMock(); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); + Query query = mock(Query.class); final List list = new ArrayList(); list.add("test"); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.setFlushMode(FlushMode.MANUAL); - sessionControl.setVoidCallable(1); - session.connection(); - sessionControl.setReturnValue(con, 2); - con.setReadOnly(true); - conControl.setVoidCallable(1); - session.createQuery("some query string"); - sessionControl.setReturnValue(query, 1); - query.list(); - queryControl.setReturnValue(list, 1); - tx.commit(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - con.isReadOnly(); - conControl.setReturnValue(true, 1); - con.setReadOnly(false); - conControl.setVoidCallable(1); - session.close(); - sessionControl.setReturnValue(null, 1); - - conControl.replay(); - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); - queryControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.connection()).willReturn(con); + given(session.isOpen()).willReturn(true); + given(session.createQuery("some query string")).willReturn(query); + given(query.list()).willReturn(list); + given(session.isConnected()).willReturn(true); + given(con.isReadOnly()).willReturn(true); HibernateTransactionManager tm = new HibernateTransactionManager(sf); TransactionTemplate tt = new TransactionTemplate(tm); @@ -782,48 +680,30 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); - conControl.verify(); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); - queryControl.verify(); + verify(session).setFlushMode(FlushMode.MANUAL); + verify(con).setReadOnly(true); + verify(tx).commit(); + verify(con).setReadOnly(false); + verify(session).close(); } + @Test public void testTransactionCommitWithFlushFailure() throws Exception { - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - tx.commit(); + given(sf.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); SQLException sqlEx = new SQLException("argh", "27"); Exception rootCause = null; ConstraintViolationException jdbcEx = new ConstraintViolationException("mymsg", sqlEx, null); - txControl.setThrowable(jdbcEx, 1); rootCause = jdbcEx; - session.close(); - sessionControl.setReturnValue(null, 1); - tx.rollback(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - session.connection(); - sessionControl.setReturnValue(con, 2); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); - conControl.replay(); + willThrow(jdbcEx).given(tx).commit(); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); HibernateTransactionManager tm = new HibernateTransactionManager(sf); TransactionTemplate tt = new TransactionTemplate(tm); @@ -850,50 +730,25 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); - conControl.verify(); + + verify(session).close(); + verify(tx).rollback(); } + @Test public void testTransactionCommitWithPreBound() throws Exception { - MockControl dsControl = MockControl.createControl(DataSource.class); - final DataSource ds = (DataSource) dsControl.getMock(); - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + final DataSource ds = mock(DataSource.class); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + final ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); - session.getFlushMode(); - sessionControl.setReturnValue(FlushMode.AUTO, 2); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.connection(); - sessionControl.setReturnValue(con, 3); - con.getTransactionIsolation(); - conControl.setReturnValue(Connection.TRANSACTION_READ_COMMITTED); - con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); - conControl.setVoidCallable(1); - con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); - conControl.setVoidCallable(1); - tx.commit(); - txControl.setVoidCallable(1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - session.disconnect(); - sessionControl.setReturnValue(null, 1); - - dsControl.replay(); - conControl.replay(); - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); + given(session.beginTransaction()).willReturn(tx); + given(session.isOpen()).willReturn(true); + given(session.getFlushMode()).willReturn(FlushMode.MANUAL); + given(session.connection()).willReturn(con); + given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_COMMITTED); + given(session.isConnected()).willReturn(true); HibernateTransactionManager tm = new HibernateTransactionManager(); tm.setSessionFactory(sf); @@ -928,55 +783,29 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); - dsControl.verify(); - conControl.verify(); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); + InOrder ordered = inOrder(session, con); + ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); + ordered.verify(session).setFlushMode(FlushMode.AUTO); + ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + ordered.verify(session).setFlushMode(FlushMode.MANUAL); + verify(tx).commit(); + verify(session).disconnect(); } + @Test public void testTransactionRollbackWithPreBound() throws Exception { - MockControl dsControl = MockControl.createControl(DataSource.class); - final DataSource ds = (DataSource) dsControl.getMock(); - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl tx1Control = MockControl.createControl(Transaction.class); - final Transaction tx1 = (Transaction) tx1Control.getMock(); - MockControl tx2Control = MockControl.createControl(Transaction.class); - final Transaction tx2 = (Transaction) tx2Control.getMock(); + final DataSource ds = mock(DataSource.class); + Connection con = mock(Connection.class); + final SessionFactory sf = mock(SessionFactory.class); + final ImplementingSession session = mock(ImplementingSession.class); + final Transaction tx1 = mock(Transaction.class); + final Transaction tx2 = mock(Transaction.class); - session.getFlushMode(); - sessionControl.setReturnValue(FlushMode.AUTO, 4); - session.beginTransaction(); - sessionControl.setReturnValue(tx1, 1); - tx1.rollback(); - tx1Control.setVoidCallable(1); - session.clear(); - sessionControl.setVoidCallable(1); - session.beginTransaction(); - sessionControl.setReturnValue(tx2, 1); - tx2.commit(); - tx2Control.setVoidCallable(1); - - session.isConnected(); - sessionControl.setReturnValue(true, 2); - session.connection(); - sessionControl.setReturnValue(con, 6); - con.isReadOnly(); - conControl.setReturnValue(false, 2); - session.disconnect(); - sessionControl.setReturnValue(null, 2); - - dsControl.replay(); - conControl.replay(); - sfControl.replay(); - sessionControl.replay(); - tx1Control.replay(); - tx2Control.replay(); + given(session.beginTransaction()).willReturn(tx1, tx2); + given(session.isOpen()).willReturn(true); + given(session.getFlushMode()).willReturn(FlushMode.MANUAL); + given(session.isConnected()).willReturn(true); + given(session.connection()).willReturn(con); HibernateTransactionManager tm = new HibernateTransactionManager(); tm.setSessionFactory(sf); @@ -1034,49 +863,27 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); - dsControl.verify(); - conControl.verify(); - sfControl.verify(); - sessionControl.verify(); - tx1Control.verify(); - tx2Control.verify(); + verify(tx1).rollback(); + verify(tx2).commit(); + InOrder ordered = inOrder(session); + ordered.verify(session).clear(); + ordered.verify(session).setFlushMode(FlushMode.AUTO); + ordered.verify(session).setFlushMode(FlushMode.MANUAL); + ordered.verify(session).disconnect(); } + @Test public void testTransactionRollbackWithHibernateManagedSession() throws Exception { - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl tx1Control = MockControl.createControl(Transaction.class); - final Transaction tx1 = (Transaction) tx1Control.getMock(); - MockControl tx2Control = MockControl.createControl(Transaction.class); - final Transaction tx2 = (Transaction) tx2Control.getMock(); + final SessionFactory sf = mock(SessionFactory.class); + final Session session = mock(Session.class); + final Transaction tx1 = mock(Transaction.class); + final Transaction tx2 = mock(Transaction.class); - sf.getCurrentSession(); - sfControl.setReturnValue(session, 2); - session.getFlushMode(); - sessionControl.setReturnValue(FlushMode.AUTO, 4); - session.getTransaction(); - sessionControl.setReturnValue(tx1, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx1, 1); - tx1.isActive(); - tx1Control.setReturnValue(false, 1); - tx1.rollback(); - tx1Control.setVoidCallable(1); - session.getTransaction(); - sessionControl.setReturnValue(tx2, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx2, 1); - tx2.isActive(); - tx2Control.setReturnValue(false, 1); - tx2.commit(); - tx2Control.setVoidCallable(1); - - sfControl.replay(); - sessionControl.replay(); - tx1Control.replay(); - tx2Control.replay(); + given(sf.getCurrentSession()).willReturn(session); + given(session.isOpen()).willReturn(true); + given(session.getTransaction()).willReturn(tx1, tx2); + given(session.beginTransaction()).willReturn(tx1, tx2); + given(session.getFlushMode()).willReturn(FlushMode.MANUAL); HibernateTransactionManager tm = new HibernateTransactionManager(); tm.setSessionFactory(sf); @@ -1118,16 +925,19 @@ public class HibernateTransactionManagerTests extends TestCase { } }); - sfControl.verify(); - sessionControl.verify(); - tx1Control.verify(); - tx2Control.verify(); + verify(tx1).rollback(); + verify(tx2).commit(); + InOrder ordered = inOrder(session); + ordered.verify(session).setFlushMode(FlushMode.AUTO); + ordered.verify(session).setFlushMode(FlushMode.MANUAL); } + @Test public void testExistingTransactionWithPropagationNestedAndRollback() throws Exception { doTestExistingTransactionWithPropagationNestedAndRollback(false); } + @Test public void testExistingTransactionWithManualSavepointAndRollback() throws Exception { doTestExistingTransactionWithPropagationNestedAndRollback(true); } @@ -1135,60 +945,27 @@ public class HibernateTransactionManagerTests extends TestCase { private void doTestExistingTransactionWithPropagationNestedAndRollback(final boolean manualSavepoint) throws Exception { - MockControl dsControl = MockControl.createControl(DataSource.class); - final DataSource ds = (DataSource) dsControl.getMock(); - MockControl conControl = MockControl.createControl(Connection.class); - Connection con = (Connection) conControl.getMock(); - MockControl mdControl = MockControl.createControl(DatabaseMetaData.class); - DatabaseMetaData md = (DatabaseMetaData) mdControl.getMock(); - MockControl spControl = MockControl.createControl(Savepoint.class); - Savepoint sp = (Savepoint) spControl.getMock(); - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); - MockControl queryControl = MockControl.createControl(Query.class); - Query query = (Query) queryControl.getMock(); + final DataSource ds = mock(DataSource.class); + Connection con = mock(Connection.class); + DatabaseMetaData md = mock(DatabaseMetaData.class); + Savepoint sp = mock(Savepoint.class); + final SessionFactory sf = mock(SessionFactory.class); + ImplementingSession session = mock(ImplementingSession.class); + Transaction tx = mock(Transaction.class); + Query query = mock(Query.class); final List list = new ArrayList(); list.add("test"); - con.isReadOnly(); - conControl.setReturnValue(false, 1); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.connection(); - sessionControl.setReturnValue(con, 3); - md.supportsSavepoints(); - mdControl.setReturnValue(true, 1); - con.getMetaData(); - conControl.setReturnValue(md, 1); - con.setSavepoint(ConnectionHolder.SAVEPOINT_NAME_PREFIX + 1); - conControl.setReturnValue(sp, 1); - con.rollback(sp); - conControl.setVoidCallable(1); - session.createQuery("some query string"); - sessionControl.setReturnValue(query, 1); - query.list(); - queryControl.setReturnValue(list, 1); - session.isConnected(); - sessionControl.setReturnValue(true, 1); - session.close(); - sessionControl.setReturnValue(null, 1); - tx.commit(); - txControl.setVoidCallable(1); - - dsControl.replay(); - conControl.replay(); - mdControl.replay(); - spControl.replay(); - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); - queryControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); + given(session.connection()).willReturn(con); + given(session.isOpen()).willReturn(true); + given(md.supportsSavepoints()).willReturn(true); + given(con.getMetaData()).willReturn(md); + given(con.setSavepoint(ConnectionHolder.SAVEPOINT_NAME_PREFIX + 1)).willReturn(sp); + given(session.createQuery("some query string")).willReturn(query); + given(query.list()).willReturn(list); + given(session.isConnected()).willReturn(true); HibernateTransactionManager tm = new HibernateTransactionManager(); tm.setNestedTransactionAllowed(true); @@ -1228,16 +1005,14 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); - dsControl.verify(); - conControl.verify(); - mdControl.verify(); - spControl.verify(); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); - queryControl.verify(); + + verify(con).setSavepoint(ConnectionHolder.SAVEPOINT_NAME_PREFIX + 1); + verify(con).rollback(sp); + verify(session).close(); + verify(tx).commit(); } + @Test public void testTransactionCommitWithNonExistingDatabase() throws Exception { final DriverManagerDataSource ds = new DriverManagerDataSource(); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean(); @@ -1279,6 +1054,7 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); } + @Test public void testTransactionCommitWithPreBoundSessionAndNonExistingDatabase() throws Exception { final DriverManagerDataSource ds = new DriverManagerDataSource(); LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean(); @@ -1328,6 +1104,7 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); } + @Test public void testTransactionCommitWithNonExistingDatabaseAndLazyConnection() throws Exception { DriverManagerDataSource dsTarget = new DriverManagerDataSource(); final LazyConnectionDataSourceProxy ds = new LazyConnectionDataSourceProxy(); @@ -1370,28 +1147,14 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); } + @Test public void testTransactionFlush() throws Exception { - MockControl sfControl = MockControl.createControl(SessionFactory.class); - final SessionFactory sf = (SessionFactory) sfControl.getMock(); - MockControl sessionControl = MockControl.createControl(ImplementingSession.class); - final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); - MockControl txControl = MockControl.createControl(Transaction.class); - Transaction tx = (Transaction) txControl.getMock(); + final SessionFactory sf = mock(SessionFactory.class); + final Session session = mock(Session.class); + Transaction tx = mock(Transaction.class); - sf.openSession(); - sfControl.setReturnValue(session, 1); - session.beginTransaction(); - sessionControl.setReturnValue(tx, 1); - session.flush(); - sessionControl.setVoidCallable(1); - tx.commit(); - txControl.setVoidCallable(1); - session.close(); - sessionControl.setReturnValue(null, 1); - - sfControl.replay(); - sessionControl.replay(); - txControl.replay(); + given(sf.openSession()).willReturn(session); + given(session.beginTransaction()).willReturn(tx); HibernateTransactionManager tm = new HibernateTransactionManager(sf); tm.setPrepareConnection(false); @@ -1411,17 +1174,10 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); - sfControl.verify(); - sessionControl.verify(); - txControl.verify(); - } - @Override - protected void tearDown() { - assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty()); - assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); - assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); - assertFalse(TransactionSynchronizationManager.isActualTransactionActive()); + verify(session).flush(); + verify(tx).commit(); + verify(session).close(); }