0

I want to support rollback when an exception occurs in my code.

I use junit + datasource in Spring config file for testing and Glassfish 2.1 for the real code (using jndi datasource).

Here a sample of the code.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-web-spring-config.xml" })
public class PersistTest {

    @Autowired
    Transformer transformer;

    @Before
    public void setUp() throws Exception {

    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    @Transactional("transactionManagerTest")
    //@Rollback(false)
    // @Ignore
    public void test() {

        transformer.export();

    }

}

@Component
public class Transformer {

    @Autowired
    ContextPersist context;

    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
    public void export(){
        //code here
        // persist here
        context.persist();
        // to test a rollback
        throw new RuntimeException("testing rollback2");

    }

}

@Component
public class ContextPersist {

    @Autowired
    @Qualifier(value = "dataSource")
    DataSource dataSource;

    // bulk insert
    JdbcTemplate jdbcTemplate;

    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
    public void persist() {
        jdbcTemplate = new JdbcTemplate(dataSource);

        //.. here I insert data with jdbcTemplate.batchUpdate(....)

        // to test a rollback
        throw new RuntimeException("testing rollback1");
    }
}

That code doesn't rollback.

If I use @Rollback(true) in my Junit, the transaction will rollback. But I need the same behavior outside a JUnit.

EDITED : (added the spring config)

My project contains a webapp (demo.war) and a jar for DAO+businessrules

In my webapp, I have my transformer.

I have a parent Spring config in this webapp, and a common spring config shared with others webapps.

Here the files.

demo.war

web-spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
         http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">

    <context:component-scan base-package="com.test" />
    <tx:annotation-driven />
    <task:annotation-driven/>

    <import resource="classpath:common-spring-config.xml" />

</beans>

DAO.jar

common-spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
        http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <context:component-scan base-package="com.test" />
    <tx:annotation-driven />
    <task:annotation-driven/>

    <!-- Hibernate -->
    <bean id="hibernateSessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="demo.datasource" />
        <property name="configLocation">
            <value>classpath:hibernate.cfg.xml</value>
        </property>
        <property name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="hibernateSessionFactory" />
    </bean>

    <!-- datasource -->
    <bean id="demo.datasource" class="org.springframework.jndi.JndiObjectFactoryBean"
        lazy-init="true">
        <property name="jndiName" value="jdbc/demo" />
    </bean>

</beans>
Sebastien Dionne
  • 757
  • 2
  • 19
  • 34
  • 2
    `@Rollback` is only relevant in a test environment. It means nothing to a Spring application. – Sotirios Delimanolis Oct 16 '13 at 18:32
  • agree. I just read about that. But the problem is still present in Glassfish. My code doesn't rollback. – Sebastien Dionne Oct 16 '13 at 19:25
  • I need to get the call : transformer.export(); to rollback . In JUNIT and in Glassfish (outside a junit). I'm trying to test that my code won't commit data if a exception occur. That's why I hardcoded a throw Exception in my code. – Sebastien Dionne Oct 16 '13 at 23:37
  • 1
    Which database are you using and please post your configuration. Note your `ContextPersist` class shouldn't construct a `JdbcTemplate` each time it is needed, it is a heavy object to create (so create it once) and next to that it is thread safe. Next to that make sure you use classbased proxies (add cglib and specify `proxy-target-class="true"` to your ``. – M. Deinum Oct 17 '13 at 18:22
  • I confirm that my transformer is thread safe. I know that jdbctemplate shouldn't be recreated, but it's only the simplify the code in this demo. It will run only one. But thanks for the remark. Look like my code is lacking the proxy-target param. I'll try it see if it fix the problem. – Sebastien Dionne Oct 18 '13 at 12:07
  • finally, adding : didn't solve my issue – Sebastien Dionne Oct 18 '13 at 16:10
  • try removing "rollbackFor=Exception.class". – Titi Wangsa Bin Damhore Nov 13 '13 at 10:33

1 Answers1

0

your transaction manager is

org.springframework.orm.hibernate3.HibernateTransactionManager

but you use JdbcTemplate

the AOP is configured to auto begin/commit/rollback on hibernate operations. jdbcTemplate would not participate in any transaction. no transaction = no rollback. its like connection.setAutoCommit(true);

  • I know, by I'm looking for to change to make it works. I don't know what I need to add the the config and code to inject Transaction in JDBCTemplate in my demo. – Sebastien Dionne Nov 13 '13 at 14:08
  • if you only want one transaction manager (only jdbc operations), then you can specify the transaction manager on the @Transactional value. @Transactional("dataSourceTransactionManager") – Titi Wangsa Bin Damhore Nov 14 '13 at 03:33