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.- How to declare a transactional proxy for our bean
- How to use abstract beans to reduce the amount of code in transactional proxies
- How to take advantage of Springs new transaction namespace to apply transactional aspects to multiple beans
- How to do transaction management via code.
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:
In the next post we shall look at each of the attributes in detail.
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:
- org.springframework.transaction.annotation.Propagation - The transaction propagation type
- org.springframework.transaction.annotation.Isolation - The transaction isolation level
- readOnly - indicates if the transaction is a read only
- rollbackFor - indicates which exceptions should result in a transaction rollback
- noRollbackFor - indicates which exceptions should not result in a transaction rollback
- timeout - transaction timeout limit
<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
Nicely explained
ReplyDeleteThanks Peter
ReplyDelete