Thursday, July 13, 2017

Use of TransactionProxyFactoryBean in Springs

Generally while writing integration test, we annotate with @TransactionConfiguration and set defaultRollback to true. The rollback helps when you do not want to explicitly delete the test data after every test case.

In a project I was working on, I happened to write an integration test for bulk insert. Bulk insert to the table is done by dumping the records in the stage table. There is a commons library in the firm that is used inorder to create this stage table.

I observed that the rollback was not happening only with the bulk insert test cases. At first I thought - we are creating stage table. which means there is a create statement executed. And since DDL statements are implicitly committed, we will not be able to rollback. But later I found that in sql server, DDLs are not implicitly committed

So when I actually went through the stageUtils source code, I found the cause was connection.commit() it was invoking and not the fact the statement was DDL, that is not causing the rollback.

How to fix it?

1) stageUtils is not something the project owns. Hence there is no way I can modify that.
2) For my transaction to rollback, I need to treat this stage table creation as a new transaction. The way you can achieve it, is by annotation stageUtils with @Transactional(propagation = Propagation.REQUIRES_NEW)

But note that I cannot do 2) because of 1)

Here is where TransactionProxyFactoryBean comes for rescue. This enabling creating proxy around the stageUtils and define required transaction attributes.

<bean id="stageUtils" class="com.foo.infra.util.StageUtils"
    scope="prototype"/>
<bean id="stageUtilsProxy"
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager" ref="someTransactionManager" />
    <property name="target" ref="stageUtils" />
    <property name="transactionAttributes">
        <props>
            <prop key="*">PROPAGATION_REQUIRES_NEW</prop>
        </props>
    </property>
</bean>
<bean id="myDAO"
    class="com.foo.myproject.dao.someDAOImpl">
        <!-- BEFORE -->
        <!-- <property name="stageUtils" ref="stageUtils" /> -->
        <!-- AFTER -->
        <property name="stageUtils" ref="stageUtilsProxy" />
</bean>


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TransactionConfiguration(transactionManager = "someTransactionManager", defaultRollback = true)
@Transactional
public abstract class BaseIntegrationDAOTest

Propagation requires new will force the stageUtils to start a new transaction and hence it won't disturb your transaction rollback

No comments:

Post a Comment