Search This Blog

Monday 8 October 2012

Spring's @Transactional annotation

Up till now we have seen various ways to achieve transactional behavior in Spring. We saw
Now to do things the easiest way - using annotations.
Since version 2.0 Spring came up with annotations to manage declarative transactions. I decided to use JDBC to test out the annotations.
The XML configuration for the same would be:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg ref="c3pDataSource" />
</bean>

<bean id="simplePersonDAO" class="com.data.dao.SimplePersonDAO">
    <property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>

<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="c3pDataSource" />
</bean>
In case of JDBC the DataSourceTransactionManager manages transactions by making calls on the java.sql.Connection object retrieved from the dataSource. A successful transaction is committed by calling the commit() method on the connection. A failed transaction is rolled back by calling the rollback() method. Consider the below example code:
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void testPersonLifecycle() {
    Person person = new Person();
    person.setName("Raj");
    person.setAge(26);
    personDAO.save(person);
    Long personId =  person.getId();
    System.out.println("Person was saved with id " + personId);
    person.setName("Raja");
    personDAO.updatePerson(person);
    System.out.println("Person with id " +personId + " was updated");
        
    person = personDAO.getPersonById(personId);
    System.out.println("Person with id " + personId + " is " + person.getName() + " and age is " + person.getAge());
    personDAO.delete(person);
    System.out.println("person with id " + personId + " has been deleted successfully");
}
The important attributes of the annotation include:
  1. org.springframework.transaction.annotation.Propagation - The transaction propagation type
  2.  org.springframework.transaction.annotation.Isolation -  The transaction isolation level
  3. readOnly - indicates if the transaction is a read only
  4. rollbackFor - indicates which exceptions should result in a transaction rollback
  5. noRollbackFor - indicates which exceptions should not result in a transaction rollback
  6. timeout - transaction timeout limit
To ensure the working of annotation driven transactions we also need to add one more element in the XML file.
<tx:annotation-driven transaction-manager="transactionManager"/>
I tested the above code:
public static void main(String[] args) {
    final ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-jdbc-txn.xml");
    final JDBCClient jdbcClient =  (JDBCClient) applicationContext.getBean("jdbcClient");
    jdbcClient.testPersonLifecycle();
    jdbcClient.testPersonForRollback();    
}
The logs indicate the execution of code:
3687 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy  
- Creating CGLIB2 proxy: target source is SingletonTargetSource 
for target object [com.test.JDBCClient@1cef4f7]
4047 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  
- Returning cached instance of singleton bean 
'org.springframework.transaction.interceptor.TransactionInterceptor#0'
4078 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy  
- Unable to apply any optimisations to advised method:
 public void com.test.JDBCClient.testPersonLifecycle()
4078 [main] DEBUG org.springframework.transaction.annotation.AnnotationTransactionAttributeSource  
- Adding transactional method 'testPersonForRollback' with attribute:
 PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
4078 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy  
- Unable to apply any optimisations to advised method: public void com.test.JDBCClient.testPersonForRollback()
...
4343 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  
- Creating new transaction with name [com.test.JDBCClient.testPersonLifecycle]:
 PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
5390 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  
- Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@691dee] for JDBC transaction
5406 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  
- Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@691dee] to manual commit

5453 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor  
- Getting transaction for [com.test.JDBCClient.testPersonLifecycle]
...
person with id 1 has been deleted successfully
6140 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor  
- Completing transaction for [com.test.JDBCClient.testPersonLifecycle]
...
***** NOW CAUSING ROLLBACK !! *****
6281 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor  
- Completing transaction for [com.test.JDBCClient.testPersonForRollback] after exception: java.lang.RuntimeException
6281 [main] DEBUG org.springframework.transaction.interceptor.RuleBasedTransactionAttribute  
- Applying rules to determine whether transaction should rollback on java.lang.RuntimeException
6281 [main] DEBUG org.springframework.transaction.interceptor.RuleBasedTransactionAttribute  
- Winning rollback rule is: null
6281 [main] DEBUG org.springframework.transaction.interceptor.RuleBasedTransactionAttribute  
- No relevant rollback rule found: applying default rules
6281 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  
- Triggering beforeCompletion synchronization
6281 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  
- Initiating transaction rollback
In the next post we shall look at each of the attributes in detail.

2 comments: