Question

I want to test if my Spring Transaction Management using Spring JDBC is working properly when one of the database updates fails. Following is my code to update two DB tables: person and contact_info

public void createWithContactInfo(String username, String name, Date dob,
            String contactName, String contactPhone, String contactEmail) {

        try {
            String sqlStmt = "INSERT INTO person (username, name, dob) VALUES (?, ?, ?)";
            jdbcTemplateObject.update(sqlStmt, "paul", "Paul", dob);

            sqlStmt = "INSERT INTO contact_info(username, customer_name, contact_name, contact_phone, contact_email) VALUES (?, ?, ?, ?, ?)";
            jdbcTemplateObject.update(sqlStmt, username, name, contactName,
                    contactPhone, contactEmail);

        } catch (DataAccessException e) {
            e.printStackTrace();
        }
    }

I use the Spring declarative transaction management to configure the beans:

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="createWithContactInfo"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="createOperation"
        expression="execution(* com.example.db.CustomerJDBCTemplate.createWithContactInfo(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="createOperation" />
</aop:config>

<!-- Initialization for data source -->
<bean id="dataSource"
    class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/Customer" />
    <property name="username" value="myusername"/>
    <property name="password" value="12345"/>
    <property name="initialSize" value="10"/>
</bean>

<!-- Definition for customerJDBCTemplate bean -->
<bean id="customerJDBCTemplate" class="com.example.db.CustomerJDBCTemplate">
    <property name="dataSource" ref="dataSource" />
</bean>

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

Then in my test code, I have:

public class JdbcTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        CustomerDAO dao = (CustomerDAO) ctx.getBean("customerJDBCTemplate");
        Date dob = new Date(90, 9, 10);
        dao.createWithContactInfo("m9087", "Sam", dob, "David", "123456", "a123@example.com");
    }
}

After running the main program, I got the Exception saying that Duplicate entry 'm9087' for key 'PRIMARY'. This is expected because m9087 already exists in the contact_info table. But since the second DB insert fails in the transaction, I thought the first jdbcTemplateObject.update(sqlStmt, "paul", "Paul", dob); will not be committed in the transaction. However I checked the person table and it returns valid entry for username=paul:

SELECT * FROM person WHERE username='paul'; 

This means the first DB insert was successful even though the second DB insert failed due to duplicate key exception.

My understanding is that the transaction should rollback and no commit will be made if any of the DB operation fail. But in this case, even though the second DB update fails due to duplicate key exception, the first DB insert still succeeded. Isn't it wrong behavior of transaction management? Is my setup correct in transaction management?

Was it helpful?

Solution

It's not rolling back because you're catching the exception. Transactions are rolled back when unchecked exception is thrown. And you are swallowing it with your catch block:

catch (DataAccessException e) {
    e.printStackTrace();
}

OTHER TIPS

The JdbcTemplate does not create automatically an transaction that envelop your both update statements in one transaction.

@Autowire
PlatformTransactionManager transactionManager;

public void createWithContactInfo(String username, String name, Date dob,
            String contactName, String contactPhone, String contactEmail) {

   DefaultTransactionDefinition paramTransactionDefinition =
                                      new DefaultTransactionDefinition();

   TransactionStatus status =
             transactionManager.getTransaction(paramTransactionDefinition);
   try{
       ... your 2 statmenets here ...

       platformTransactionManager.commit(status);
   }catch (Exception e) {
      platformTransactionManager.rollback(status);
   }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top