1

I have created an integration test of a service that is not rolling back the transaction after completion. I know this from looking at the db and from my the error I get when I run the test a second time. I've been googling this issue all morning and feel I have everything set properly. This is a hibernate/jpa app writing to SQLServer 2008. I am not sure where else to look. Snippet below.

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({
        DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionalTestExecutionListener.class })
@TransactionConfiguration(defaultRollback=true)
@Transactional // extend transactional boundary to test class so that automatic rollback works properly
@ContextConfiguration(locations = {
        "file:./src/main/resources/AghostMobile.Service-business.service-context.xml",
        "file:./src/main/resources/AghostMobile.Service-service-context.xml",
        "file:./src/main/resources/AghostMobile.Service-dao-context.xml"})
public class ColorSchemeMigrationServiceIntTest {

    /**
     * The service being tested, injected by Spring
     *
     */
    @Autowired
    ColorSchemeMigrationService service;

    /**
     * The helper services, injected by Spring.
     *
     */
    @Autowired
    protected WebsitecolorpaletteuserdefinedService userPaletteService;
    @Test
    public void testSaveColorPalette() {
        Integer mobileWebsiteId = Integer.valueOf(386);
        Integer custId = Integer.valueOf(15);
        Integer siteId = Integer.valueOf(2);
        String user = "Test";

        Websitecolorpaletteuserdefined palette = service.translateColorScheme(mobileWebsiteId, custId, siteId, user);

        service.saveColorPalette(palette);

        Websitecolorpaletteuserdefined response = userPaletteService.findWebsitecolorpaletteuserdefinedByCustIdAndSiteId(custId, siteId);

        assertNotNull("User palette not found.", response);
        assertEquals("CustId is not the expected value.", custId, response.getCustId());
        assertEquals("SiteId is not the expected value.", siteId, response.getSiteId());
    }

Currently, I have the following beans defined:

<!-- ******************************************************************** -->
<!-- Setup the transaction manager -->
<!-- ******************************************************************** -->
    <!-- Using Atomikos Transaction Manager -->
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init"
        destroy-method="close">
        <property name="forceShutdown" value="true" />
        <property name="startupTransactionService" value="true" />
        <property name="transactionTimeout" value="60" />
    </bean>

    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" />
    <!-- Configure the Spring framework to use JTA transactions from Atomikos -->
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="atomikosTransactionManager" />
        <property name="userTransaction" ref="atomikosUserTransaction" />
        <property name="transactionSynchronizationName" value="SYNCHRONIZATION_ON_ACTUAL_TRANSACTION" />
    </bean>
<!-- ******************************************************************** -->
<!-- Setup a data source -->
<!-- ******************************************************************** -->
<!-- Using Apache DBCP Data Sources -->
<bean name="hostDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >
    <property name="driverClassName" value="${My_JDTs_to_AgHost_Host_scheme.connection.driver_class}" />
    <property name="username" value="${My_JDTs_to_AgHost_Host_scheme.connection.username}" />
    <property name="password" value="${My_JDTs_to_AgHost_Host_scheme.connection.password}" />
    <property name="url" value="${My_JDTs_to_AgHost_Host_scheme.connection.url}" />
    <property name="maxIdle" value="${My_JDTs_to_AgHost_Host_scheme.minPoolSize}" />
    <property name="maxActive" value="${My_JDTs_to_AgHost_Host_scheme.maxPoolSize}" />
</bean>

<!-- ******************************************************************** -->
<!-- Setup each persistence unit -->
<!-- ******************************************************************** -->
            <!-- Configure a JPA vendor adapter -->
            <bean id="My_JDTs_to_AgHost_Host_schemeJPAVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="${My_JDTs_to_AgHost_Host_scheme.show_sql}" />
                <property name="generateDdl" value="${My_JDTs_to_AgHost_Host_scheme.generateDdl}" />
                <property name="databasePlatform" value="${My_JDTs_to_AgHost_Host_scheme.dialect}" />
            </bean>
            <!-- EntityManager Factory that brings together the persistence unit, datasource, and JPA Vendor -->
            <bean id="My_JDTs_to_AgHost_Host_scheme" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
                <property name="dataSource" ref="hostDataSource" />
                <property name="persistenceUnitName" value="My_JDTs_to_AgHost_Host_scheme" />
                <property name="jpaVendorAdapter" ref="My_JDTs_to_AgHost_Host_schemeJPAVendorAdapter" />
                    <property name="jpaPropertyMap">
                        <map>
                                    <entry key="hibernate.transaction.manager_lookup_class" value="com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup" />
                                    <!-- <entry key="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory" /> -->
                                    <entry key="hibernate.connection.release_mode" value="on_close" />
                        </map>
                    </property>
            </bean>

This does allow me to update my data, but does not rollback my transaction. So, I am using a transaction manager, org.springframework.jdbc.datasource.DataSourceTransactionManager. I replaced the three beans in the "Setup transaction manager" block with:

    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="hostDataSource" />
    </bean>

This left me with tests failing with IllegalStateException: This method needs a transaction for the calling thread and none exists. The exception also stated:

Possible causes: either you didn't start a transaction,
it rolledback due to timeout, or it was committed already.
ACTIONS: You can try one of the following: 
1. Make sure you started a transaction for the thread.
2. Make sure you didn't terminate it yet.
3. Increase the transaction timeout to avoid automatic rollback of long transactions;
   check [http://www.atomikos.com/Documentation/JtaProperties][1] for how to do this.

I'll admit to not having read that document yet and will do so after posting these updates. I have also found this thread, which looked promising: persistence-unit, different hibernate.transaction.manager_lookup_class property. You can see that commented out in bean My_JDTs_to_AgHost_Host_scheme. This failed pretty miserably. But, perhaps I didn't use it properly.

I also found this thread: Spring/JTA/JPA DAO integration test doesn't rollback?. This looks quite promising, but again I am not sure how to use what it is telling me.

Community
  • 1
  • 1
Bill Turner
  • 869
  • 1
  • 13
  • 27
  • 1
    Have you tried using the `@Transactional` on each test method instead of the entire class? Do you expect data to be maintained in between test cases but rolled back after the class is done? Or do you expect the rollback after every method? – Jesse Webb Oct 03 '12 at 17:50
  • 1
    @JesseWebb putting the `@Trasactional` annotation at the test class level is standard practice for rolling back transactions after each test – John B Oct 03 '12 at 18:10
  • @JesseWebb - I expect tests rolled back after each test. I have the same understanding as JohnB – Bill Turner Oct 03 '12 at 18:14
  • @JohnB - Thanks for the clarification, I thought it behaved differently when applied to the class vs. each method. It looks like the OP has it in the right spot though for what he is trying to accomplish. – Jesse Webb Oct 03 '12 at 18:33
  • I've updated with research thus far. Still have no answer. – Bill Turner Oct 04 '12 at 21:31

2 Answers2

1

The answer tunred out to be found here: Spring/JTA/JPA DAO integration test doesn't rollback?. I changed the datasource to the following.

<bean name="hostDataSource" class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean" destroy-method="close" >
    <property name="driverClassName" value="${My_JDTs_to_AgHost_Host_scheme.connection.driver_class}" />
    <property name="user" value="${My_JDTs_to_AgHost_Host_scheme.connection.username}" />
    <property name="password" value="${My_JDTs_to_AgHost_Host_scheme.connection.password}" />
    <property name="url" value="${My_JDTs_to_AgHost_Host_scheme.connection.url}" />

    <property name="maxPoolSize" value="20" />
    <property name="reapTimeout" value="300" />
    <property name="uniqueResourceName" value="myappDatabase" />
</bean>
Community
  • 1
  • 1
Bill Turner
  • 869
  • 1
  • 13
  • 27
0

Do you have a DataSourceTransactionManager bean in your loaded context?

DataSourceTransactionManager example

See section 9.3

John B
  • 32,493
  • 6
  • 77
  • 98