In the previous post we saw how Spring managed caching via proxies. It follows a similar route to achieve transaction management in a very transparent way. Spring supports programmatic and declarative transactions. Declarative uses aspects and therefore proxies and is therefore achieved in a very transparent manner.
Transaction management whether via code or declarative advises involves the use of a transaction manager. Spring on its own hasn't created any transaction framework. It merely integrates with existing transaction support provided by a host of database integration technologies like JDBC, Hibernate, JDO, JPA and many more. For each of these varying technologies Spring has defined a Transaction Manager.
I decided to test transaction management for Hibernate. The first step is to create the Transaction Manager.
I decided to add transactional capabilities to my previous test class.
The logs of the execution are as below:
Transaction management whether via code or declarative advises involves the use of a transaction manager. Spring on its own hasn't created any transaction framework. It merely integrates with existing transaction support provided by a host of database integration technologies like JDBC, Hibernate, JDO, JPA and many more. For each of these varying technologies Spring has defined a Transaction Manager.
I decided to test transaction management for Hibernate. The first step is to create the Transaction Manager.
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>The sessionFactory bean is a LocalSessionFactoryBean as defined in our hibernate post. As can be seen above the TransactionManager takes a reference to the sessionFactory. This allows the manager to get access to the current session and therefore the transaction.
I decided to add transactional capabilities to my previous test class.
public class HibProxyClient { @Autowired private IPersonDAO personDAO; 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"); } public void testPersonForRollback() { Person person = new Person(); person.setName("Rim"); person.setAge(21); personDAO.save(person); Long personId = person.getId(); System.out.println("Person was saved with id " + personId); System.out.println("***** NOW CAUSING ROLLBACK !! *****"); throw new RuntimeException(); } }The configurations are made in the XML file:
<bean id="hibProxyClient" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="txManager" /> <property name="target" ref="hibProxyClientTarget" /> <property name="transactionAttributes"> <props> <prop key="test*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <!-- The actual bean --> <bean id ="hibProxyClientTarget" class ="com.test.HibProxyClient"> </bean>As can be seen above we defined two beans - the hibProxyClient which is a proxy to manage transactional behavior and hibProxyClientTarget - the actual POJO, the one that needs transaction support.The proxy involves the following properties:
- a reference to the Transaction Manager
- a reference to the target bean
- the details of the methods being proxied. In our case we need all methods which begin with "test" to be run within a transactional boundary.
public static void main(String[] args) { final ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("spring-transactional.xml"); final HibProxyClient hibClient = (HibProxyClient)
applicationContext.getBean("hibProxyClient"); hibClient.testPersonLifecycle(); hibClient.testPersonForRollback(); }In the above test code, we loaded the proxy and not the actual class. All calls therefore are directed through the proxy.
The logs of the execution are as below:
5766 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean hibProxyClientTarget 5782 [main] DEBUG org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource - Adding transactional method [test*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]The Spring container created the proxy and also loads the details of the tranactional settings. For proxy creation, spring used CGLIB library:
5813 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy
- Creating CGLIB2 proxy: target source is SingletonTargetSource
for target object [com.test.HibProxyClient@155d3a3] 6016 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy - Unable to apply any optimisations to advised method:
public void com.test.HibProxyClient.testPersonLifecycle() 6016 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy - Unable to apply any optimisations to advised method:
public void com.test.HibProxyClient.testPersonForRollback()
6016 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy
- Found 'hashCode' method: public native int java.lang.Object.hashCode()
6016 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy
- Found finalize() method - using NO_OVERRIDE
6016 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy
- Unable to apply any optimisations to advised method: protected
native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException 6016 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy - Found 'equals' method: public boolean java.lang.Object.equals(java.lang.Object) 6016 [main] DEBUG org.springframework.aop.framework.Cglib2AopProxy - Unable to apply any optimisations to advised method:
public java.lang.String java.lang.Object.toString()When we called the testPersonLifecycle() method:
6110 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager
- Creating new transaction with name [com.test.HibProxyClient.testPersonLifecycle]
: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
6188 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager
- Opened new Session [org.hibernate.impl.SessionImpl@cbd8dc] for Hibernate transaction
6188 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager
- Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@cbd8dc]
6235 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager
- Exposing Hibernate transaction as JDBC transaction
[com.mchange.v2.c3p0.impl.NewProxyConnection@13a34af]
...
6250 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager
- Initializing transaction synchronization
6250 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor
- Getting transaction for [com.test.HibProxyClient.testPersonLifecycle]
On method completion:6719 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Completing transaction for [com.test.HibProxyClient.testPersonLifecycle] 6719 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Triggering beforeCommit synchronization 6735 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Triggering beforeCompletion synchronization 6735 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Initiating transaction commit 6735 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@cbd8dc] 6750 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Triggering afterCommit synchronization 6750 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Triggering afterCompletion synchronization 6750 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Clearing transaction synchronizationAfter this I called the testPersonForRollback() method:
5641 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Creating new transaction with name [com.test.HibProxyClient.testPersonForRollback] : PROPAGATION_REQUIRED,ISOLATION_DEFAULT ... 5875 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [com.test.HibProxyClient.testPersonForRollback] ... 5938 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@13a34af] for key [org.hibernate.impl.SessionFactoryImpl@135f44e] bound to thread [main]The code as can be seen throws an exception:
Exception in thread "main" java.lang.RuntimeException
at com.test.HibProxyClient.testPersonForRollback(HibProxyClient.java:46)
at com.test.HibProxyClient$$FastClassByCGLIB$$68c50ea7.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation
.invokeJoinpoint(Cglib2AopProxy.java:688)Spring by default rollbacks on runtime exceptions. So the logs indicate:
Person was saved with id 12
***** NOW CAUSING ROLLBACK !! *****
6047 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor
- Completing transaction for [com.test.HibProxyClient.testPersonForRollback]
after exception: java.lang.RuntimeException
6047 [main] DEBUG org.springframework.transaction
.interceptor.RuleBasedTransactionAttribute
- Applying rules to determine whether transaction
should rollback on java.lang.RuntimeException
6047 [main] DEBUG org.springframework.transaction
.interceptor.RuleBasedTransactionAttribute
- Winning rollback rule is: null
6250 [main] DEBUG org.springframework.transaction
.interceptor.RuleBasedTransactionAttribute - No relevant rollback rule found: applying default rules 6250 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Triggering beforeCompletion synchronization 6250 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Initiating transaction rollback 6250 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Rolling back Hibernate transaction on Session
[org.hibernate.impl.SessionImpl@20807c] 6266 [main] DEBUG org.springframework.orm.hibernate3.HibernateTransactionManager - Triggering afterCompletion synchronization 6266 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Clearing transaction synchronization
No comments:
Post a Comment