org.springframework.orm.hibernate4.HibernateTransactionManagerTests.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.orm.hibernate4.HibernateTransactionManagerTests.java

Source

/*
 * 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.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.orm.hibernate4;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
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;
import javax.transaction.UserTransaction;

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.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;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.transaction.support.TransactionCallback;
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
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
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 {
        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");
        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
            protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {
                return sf;
            }
        };
        lsfb.afterPropertiesSet();
        final SessionFactory sfProxy = lsfb.getObject();

        HibernateTransactionManager tm = new HibernateTransactionManager();
        tm.setSessionFactory(sfProxy);
        tm.setDataSource(ds);
        TransactionTemplate tt = new TransactionTemplate(tm);
        tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
        tt.setTimeout(10);
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        Object result = tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sfProxy));
                assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
                Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sfProxy))
                        .getSession();
                return session.createQuery("some query string").list();
            }
        });
        assertTrue("Correct result list", result == list);

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        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 {
        Connection con = mock(Connection.class);
        final SessionFactory sf = mock(SessionFactory.class);
        ImplementingSession session = mock(ImplementingSession.class);
        Transaction tx = mock(Transaction.class);

        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);
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        try {
            tt.execute(new TransactionCallback() {
                @Override
                public Object doInTransaction(TransactionStatus status) {
                    assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                    throw new RuntimeException("application exception");
                }
            });
            fail("Should have thrown RuntimeException");
        } catch (RuntimeException ex) {
            // expected
        }

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
        verify(session).close();
        verify(tx).rollback();
    }

    @Test
    public void testTransactionRollbackOnly() throws Exception {
        Connection con = mock(Connection.class);
        final SessionFactory sf = mock(SessionFactory.class);
        ImplementingSession session = mock(ImplementingSession.class);
        Transaction tx = mock(Transaction.class);

        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);
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));

        tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession();
                session.flush();
                status.setRollbackOnly();
                return null;
            }
        });

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        verify(session).flush();
        verify(session).close();
        verify(tx).rollback();
    }

    @Test
    public void testParticipatingTransactionWithCommit() throws Exception {
        Connection con = mock(Connection.class);
        final SessionFactory sf = mock(SessionFactory.class);
        final ImplementingSession session = mock(ImplementingSession.class);
        Transaction tx = mock(Transaction.class);

        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
            protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {
                return sf;
            }
        };
        lsfb.afterPropertiesSet();
        final SessionFactory sfProxy = lsfb.getObject();

        PlatformTransactionManager tm = new HibernateTransactionManager(sfProxy);
        final TransactionTemplate tt = new TransactionTemplate(tm);
        final List l = new ArrayList();
        l.add("test");

        Object result = tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                return tt.execute(new TransactionCallback() {
                    @Override
                    public Object doInTransaction(TransactionStatus status) {
                        Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf))
                                .getSession();
                        session.flush();
                        return l;
                    }
                });
            }
        });
        assertTrue("Correct result list", result == l);

        verify(session).flush();
        verify(session).close();
        verify(tx).commit();
    }

    @Test
    public void testParticipatingTransactionWithRollback() throws Exception {
        Connection con = mock(Connection.class);
        final SessionFactory sf = mock(SessionFactory.class);
        ImplementingSession session = mock(ImplementingSession.class);
        Transaction tx = mock(Transaction.class);

        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);
        try {
            tt.execute(new TransactionCallback() {
                @Override
                public Object doInTransaction(TransactionStatus status) {
                    return tt.execute(new TransactionCallback() {
                        @Override
                        public Object doInTransaction(TransactionStatus status) {
                            throw new RuntimeException("application exception");
                        }
                    });
                }
            });
            fail("Should have thrown RuntimeException");
        } catch (RuntimeException ex) {
            // expected
        }

        verify(session).close();
        verify(tx).rollback();
    }

    @Test
    public void testParticipatingTransactionWithRollbackOnly() throws Exception {
        Connection con = mock(Connection.class);
        final SessionFactory sf = mock(SessionFactory.class);
        ImplementingSession session = mock(ImplementingSession.class);
        Transaction tx = mock(Transaction.class);

        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() {
                @Override
                public Object doInTransaction(TransactionStatus status) {
                    return tt.execute(new TransactionCallback() {
                        @Override
                        public Object doInTransaction(TransactionStatus status) {
                            status.setRollbackOnly();
                            return null;
                        }
                    });
                }
            });
            fail("Should have thrown UnexpectedRollbackException");
        } catch (UnexpectedRollbackException ex) {
            // expected
        }

        verify(session).close();
        verify(tx).rollback();
    }

    @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);

        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);
        tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                final SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
                assertTrue("Has thread session", holder != null);
                assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
                tt.execute(new TransactionCallback() {
                    @Override
                    public Object doInTransaction(TransactionStatus status) {
                        Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf))
                                .getSession();
                        assertTrue("Not enclosing session", session != holder.getSession());
                        session.flush();
                        assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                        assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
                        return null;
                    }
                });
                assertTrue("Same thread session as before",
                        holder.getSession() == ((SessionHolder) TransactionSynchronizationManager.getResource(sf))
                                .getSession());
                assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
                return null;
            }
        });
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));

        verify(session2).flush();
        verify(session1).close();
        verify(session2).close();
        verify(tx, times(2)).commit();
    }

    @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);

        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);
        tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
                assertTrue("Has thread session", holder != null);
                assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
                tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
                tt.execute(new TransactionCallback() {
                    @Override
                    public Object doInTransaction(TransactionStatus status) {
                        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
                        assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                        assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
                        return null;
                    }
                });
                assertTrue("Same thread session as before",
                        holder.getSession() == ((SessionHolder) TransactionSynchronizationManager.getResource(sf))
                                .getSession());
                assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
                return null;
            }
        });
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));

        verify(session).close();
        verify(tx).commit();
    }

    @Test
    public void testTransactionWithPropagationSupports() throws Exception {
        final SessionFactory sf = mock(SessionFactory.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 = sf.openSession();
                session.flush();
                session.close();
                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 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);
        final ImplementingSession session1 = mock(ImplementingSession.class);
        final ImplementingSession session2 = mock(ImplementingSession.class);
        Connection con = mock(Connection.class);
        Transaction tx = mock(Transaction.class);

        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
            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);
        final TransactionTemplate tt2 = new TransactionTemplate(tm);
        tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        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 = sfProxy.openSession();
                assertSame(session1, session);
                tt2.execute(new TransactionCallback() {
                    @Override
                    public Object doInTransaction(TransactionStatus status) {
                        assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                        assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
                        Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf))
                                .getSession();
                        assertSame(session2, session);
                        session.flush();
                        return null;
                    }
                });
                session.flush();
                session.close();
                assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
                return null;
            }
        });
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));

        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);
        tm.setAllowResultAccessAfterCompletion(true);
        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());

        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 {
        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");
        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);
        tt.setReadOnly(true);
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        Object result = tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                assertTrue(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
                Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession();
                return session.createQuery("some query string").list();
            }
        });
        assertTrue("Correct result list", result == list);

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        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 {
        Connection con = mock(Connection.class);
        final SessionFactory sf = mock(SessionFactory.class);
        ImplementingSession session = mock(ImplementingSession.class);
        Transaction tx = mock(Transaction.class);

        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);
        rootCause = jdbcEx;
        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);
        final List l = new ArrayList();
        l.add("test");
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        try {
            tt.execute(new TransactionCallback() {
                @Override
                public Object doInTransaction(TransactionStatus status) {
                    assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                    return l;
                }
            });
            fail("Should have thrown DataIntegrityViolationException");
        } catch (DataIntegrityViolationException ex) {
            // expected
            assertEquals(rootCause, ex.getCause());
            assertTrue(ex.getMessage().contains("mymsg"));
        }

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        verify(session).close();
        verify(tx).rollback();
    }

    @Test
    public void testTransactionCommitWithPreBound() throws Exception {
        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);

        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);
        tm.setDataSource(ds);
        TransactionTemplate tt = new TransactionTemplate(tm);
        tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
        final List l = new ArrayList();
        l.add("test");
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
        TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session));
        assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));

        Object result = tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
                assertTrue("Has thread transaction", sessionHolder.getTransaction() != null);
                Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession();
                assertEquals(session, sess);
                return l;
            }
        });
        assertTrue("Correct result list", result == l);

        assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
        assertTrue("Hasn't thread transaction", sessionHolder.getTransaction() == null);
        TransactionSynchronizationManager.unbindResource(sf);
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        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 testTransactionCommitWithPreBoundAndResultAccessAfterCommit() throws Exception {
        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);

        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(con.getHoldability()).willReturn(ResultSet.CLOSE_CURSORS_AT_COMMIT);
        given(session.isConnected()).willReturn(true);

        HibernateTransactionManager tm = new HibernateTransactionManager();
        tm.setSessionFactory(sf);
        tm.setDataSource(ds);
        tm.setAllowResultAccessAfterCompletion(true);
        TransactionTemplate tt = new TransactionTemplate(tm);
        tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
        final List l = new ArrayList();
        l.add("test");
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
        TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session));
        assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));

        Object result = tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
                assertTrue("Has thread transaction", sessionHolder.getTransaction() != null);
                Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession();
                assertEquals(session, sess);
                return l;
            }
        });
        assertTrue("Correct result list", result == l);

        assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
        assertTrue("Hasn't thread transaction", sessionHolder.getTransaction() == null);
        TransactionSynchronizationManager.unbindResource(sf);
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        InOrder ordered = inOrder(session, con);
        ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
        ordered.verify(con).setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
        ordered.verify(session).setFlushMode(FlushMode.AUTO);
        ordered.verify(con).setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT);
        ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
        ordered.verify(session).setFlushMode(FlushMode.MANUAL);
        verify(tx).commit();
    }

    @Test
    public void testTransactionRollbackWithPreBound() throws Exception {
        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);

        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);
        tm.setDataSource(ds);
        final TransactionTemplate tt = new TransactionTemplate(tm);
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
        TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session));
        assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));

        try {
            tt.execute(new TransactionCallbackWithoutResult() {
                @Override
                public void doInTransactionWithoutResult(TransactionStatus status) {
                    assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                    assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                    SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
                    assertEquals(tx1, sessionHolder.getTransaction());
                    tt.execute(new TransactionCallbackWithoutResult() {
                        @Override
                        public void doInTransactionWithoutResult(TransactionStatus status) {
                            status.setRollbackOnly();
                            Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf))
                                    .getSession();
                            assertEquals(session, sess);
                        }
                    });
                }
            });
            fail("Should have thrown UnexpectedRollbackException");
        } catch (UnexpectedRollbackException ex) {
            // expected
        }

        assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
        assertTrue("Hasn't thread transaction", sessionHolder.getTransaction() == null);
        assertTrue("Not marked rollback-only", !sessionHolder.isRollbackOnly());

        tt.execute(new TransactionCallbackWithoutResult() {
            @Override
            public void doInTransactionWithoutResult(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
                assertEquals(tx2, sessionHolder.getTransaction());
                Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession();
                assertEquals(session, sess);
            }
        });

        assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
        assertTrue("Hasn't thread transaction", sessionHolder.getTransaction() == null);
        TransactionSynchronizationManager.unbindResource(sf);
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        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 {
        final SessionFactory sf = mock(SessionFactory.class);
        final Session session = mock(Session.class);
        final Transaction tx1 = mock(Transaction.class);
        final Transaction tx2 = mock(Transaction.class);

        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);
        tm.setPrepareConnection(false);
        tm.setHibernateManagedSession(true);
        final TransactionTemplate tt = new TransactionTemplate(tm);

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));

        try {
            tt.execute(new TransactionCallbackWithoutResult() {
                @Override
                public void doInTransactionWithoutResult(TransactionStatus status) {
                    assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                    tt.execute(new TransactionCallbackWithoutResult() {
                        @Override
                        public void doInTransactionWithoutResult(TransactionStatus status) {
                            status.setRollbackOnly();
                            Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf))
                                    .getSession();
                            assertEquals(session, sess);
                        }
                    });
                }
            });
            fail("Should have thrown UnexpectedRollbackException");
        } catch (UnexpectedRollbackException ex) {
            // expected
        }

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));

        tt.execute(new TransactionCallbackWithoutResult() {
            @Override
            public void doInTransactionWithoutResult(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession();
                assertEquals(session, sess);
            }
        });

        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);
    }

    private void doTestExistingTransactionWithPropagationNestedAndRollback(final boolean manualSavepoint)
            throws Exception {

        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");
        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);
        tm.setSessionFactory(sf);
        tm.setDataSource(ds);
        final TransactionTemplate tt = new TransactionTemplate(tm);
        tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        Object result = tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                if (manualSavepoint) {
                    Object savepoint = status.createSavepoint();
                    status.rollbackToSavepoint(savepoint);
                } else {
                    tt.execute(new TransactionCallbackWithoutResult() {
                        @Override
                        protected void doInTransactionWithoutResult(TransactionStatus status) {
                            assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                            assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                            status.setRollbackOnly();
                        }
                    });
                }
                Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession();
                return session.createQuery("some query string").list();
            }
        });
        assertTrue("Correct result list", result == list);

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        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();
        lsfb.setDataSource(ds);
        Properties props = new Properties();
        props.setProperty("hibernate.dialect", HSQLDialect.class.getName());
        lsfb.setHibernateProperties(props);
        lsfb.afterPropertiesSet();
        final SessionFactory sf = lsfb.getObject();

        HibernateTransactionManager tm = new HibernateTransactionManager();
        tm.setSessionFactory(sf);
        tm.afterPropertiesSet();
        TransactionTemplate tt = new TransactionTemplate(tm);
        tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
        tt.setTimeout(10);
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        try {
            tt.execute(new TransactionCallback() {
                @Override
                public Object doInTransaction(TransactionStatus status) {
                    assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                    assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                    Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf))
                            .getSession();
                    return session.createQuery("from java.lang.Object").list();
                }
            });
            fail("Should have thrown CannotCreateTransactionException");
        } catch (CannotCreateTransactionException ex) {
            // expected
        }

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
    }

    @Test
    public void testTransactionCommitWithPreBoundSessionAndNonExistingDatabase() throws Exception {
        final DriverManagerDataSource ds = new DriverManagerDataSource();
        LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
        lsfb.setDataSource(ds);
        Properties props = new Properties();
        props.setProperty("hibernate.dialect", HSQLDialect.class.getName());
        lsfb.setHibernateProperties(props);
        lsfb.afterPropertiesSet();
        final SessionFactory sf = lsfb.getObject();

        HibernateTransactionManager tm = new HibernateTransactionManager();
        tm.setSessionFactory(sf);
        tm.afterPropertiesSet();
        TransactionTemplate tt = new TransactionTemplate(tm);
        tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
        tt.setTimeout(10);
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        Session session = sf.openSession();
        TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session));
        try {
            tt.execute(new TransactionCallback() {
                @Override
                public Object doInTransaction(TransactionStatus status) {
                    assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                    assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                    Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf))
                            .getSession();
                    return session.createQuery("from java.lang.Object").list();
                }
            });
            fail("Should have thrown CannotCreateTransactionException");
        } catch (CannotCreateTransactionException ex) {
            // expected
            SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
            assertFalse(holder.isSynchronizedWithTransaction());
        } finally {
            TransactionSynchronizationManager.unbindResource(sf);
            session.close();
        }

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
    }

    @Test
    public void testTransactionCommitWithNonExistingDatabaseAndLazyConnection() throws Exception {
        DriverManagerDataSource dsTarget = new DriverManagerDataSource();
        final LazyConnectionDataSourceProxy ds = new LazyConnectionDataSourceProxy();
        ds.setTargetDataSource(dsTarget);
        ds.setDefaultAutoCommit(true);
        ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
        //ds.setDefaultTransactionIsolationName("TRANSACTION_READ_COMMITTED");

        LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
        lsfb.setDataSource(ds);
        Properties props = new Properties();
        props.setProperty("hibernate.dialect", HSQLDialect.class.getName());
        props.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");
        lsfb.setHibernateProperties(props);
        lsfb.afterPropertiesSet();
        final SessionFactory sf = lsfb.getObject();

        HibernateTransactionManager tm = new HibernateTransactionManager();
        tm.setSessionFactory(sf);
        tm.afterPropertiesSet();
        TransactionTemplate tt = new TransactionTemplate(tm);
        tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
        tt.setTimeout(10);
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        tt.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds));
                Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession();
                return session.createQuery("from java.lang.Object").list();
            }
        });

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());
    }

    @Test
    public void testTransactionFlush() throws Exception {
        final SessionFactory sf = mock(SessionFactory.class);
        final Session session = mock(Session.class);
        Transaction tx = mock(Transaction.class);

        given(sf.openSession()).willReturn(session);
        given(session.beginTransaction()).willReturn(tx);

        HibernateTransactionManager tm = new HibernateTransactionManager(sf);
        tm.setPrepareConnection(false);
        TransactionTemplate tt = new TransactionTemplate(tm);
        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        tt.execute(new TransactionCallbackWithoutResult() {
            @Override
            public void doInTransactionWithoutResult(TransactionStatus status) {
                assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf));
                assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
                assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
                status.flush();
            }
        });

        assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf));
        assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive());

        verify(session).flush();
        verify(tx).commit();
        verify(session).close();
    }

    @Test
    public void testSetJtaTransactionManager() throws Exception {
        DataSource ds = mock(DataSource.class);
        TransactionManager tm = mock(TransactionManager.class);
        UserTransaction ut = mock(UserTransaction.class);
        TransactionSynchronizationRegistry tsr = mock(TransactionSynchronizationRegistry.class);
        JtaTransactionManager jtm = new JtaTransactionManager();
        jtm.setTransactionManager(tm);
        jtm.setUserTransaction(ut);
        jtm.setTransactionSynchronizationRegistry(tsr);
        LocalSessionFactoryBuilder lsfb = new LocalSessionFactoryBuilder(ds);
        lsfb.setJtaTransactionManager(jtm);
        Object jtaPlatform = lsfb.getProperties().get(AvailableSettings.JTA_PLATFORM);
        assertNotNull(jtaPlatform);
        assertSame(tm, jtaPlatform.getClass().getMethod("retrieveTransactionManager").invoke(jtaPlatform));
        assertSame(ut, jtaPlatform.getClass().getMethod("retrieveUserTransaction").invoke(jtaPlatform));
        assertTrue(
                lsfb.getProperties().get(AvailableSettings.TRANSACTION_STRATEGY) instanceof CMTTransactionFactory);
    }

    @Test
    public void testSetTransactionManager() throws Exception {
        DataSource ds = mock(DataSource.class);
        TransactionManager tm = mock(TransactionManager.class);
        LocalSessionFactoryBuilder lsfb = new LocalSessionFactoryBuilder(ds);
        lsfb.setJtaTransactionManager(tm);
        Object jtaPlatform = lsfb.getProperties().get(AvailableSettings.JTA_PLATFORM);
        assertNotNull(jtaPlatform);
        assertSame(tm, jtaPlatform.getClass().getMethod("retrieveTransactionManager").invoke(jtaPlatform));
        assertTrue(
                lsfb.getProperties().get(AvailableSettings.TRANSACTION_STRATEGY) instanceof CMTTransactionFactory);
    }

    public interface ImplementingSession extends Session, SessionImplementor {
    }

}