In the previous post, I described different ways in which spring and hibernate can
be integrated. In this post I will describe how to use Spring's transaction features
in hibernate. The following methods of transaction management with spring and hibernate are discussed.
- Declarative Transaction Mangement with AOP Interceptors
- Schema-based Declarative Transaction Management
- Schema-based Declarative Transaction Management with Annotations
- Programmatic Transaction Management
The easiest way to check if transactions are working for this example is to remove the transaction declarations for the getStockQuote() method. Removing transactions will cause the following exception
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
There's More ...
For this example, start off with the following as described in the "Integrating Spring and Hibernate" post.
- The bean StockQuoteBean
- The Portfolio classes : PortfolioDAO, PortfolioDAOSupport and PortfolioDAOTemplate
- The hibernate mapping file stockquote.hbm.xml
The following main class can be used:package springhibernate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringHibernateTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
IPortfolioService portfolioService = (IPortfolioService) ctx.getBean("portfolioService");
System.out.println("Portfolio Service type : " + portfolioService.getClass());
portfolioService.getStockQuote("123");
}
}
SpringHibernateTest.java
Note that here we use an Interface IPortfolioService, and also note the change to ApplicationContext instead of a BeanFactory.
The reason for using ApplicationContext is enable the use of AOP for declarative transaction management. Also we are not expecting any return to the main class as all the execution is expected to happen in the transaction context.
The Portfolio Service Interface:package springhibernate;
import beans.StockQuoteBean;
public interface IPortfolioService {
public void getStockQuote(String id);
public void updateStockQuote(StockQuoteBean stockQuoteBean);
}
IPortfolioService.java
Declarative Transaction Management
Declarative transaction management in Spring has the advantage of being less invasive. There is no need for changing application code when using declarative transactions. All you have to do is to modify the application context.
The Service class will be same for all the modes of Declarative transaction management described below.package springhibernate;
import beans.StockQuoteBean;
import dao.PortfolioDAO;
public class PortfolioService implements IPortfolioService {
private PortfolioDAO portfolioDAO;
public void getStockQuote(String id) {
StockQuoteBean result = portfolioDAO.getStockQuote(id);
System.out.println("Result in Service : " + result.getStockSymbol());
}
public void updateStockQuote(StockQuoteBean stockQuoteBean) {
portfolioDAO.updateStockQuote(stockQuoteBean);
}
public PortfolioDAO getPortfolioDAO() {
return portfolioDAO;
}
public void setPortfolioDAO(PortfolioDAO portfolioDAO) {
this.portfolioDAO = portfolioDAO;
System.out.println("Setting portfolio DAO to : " + portfolioDAO.getClass());
}
}
PortfolioService.java
Declarative Transaction Management with AOP Interceptor
In this method, you have to define a proxy for the bean that will be made transactional.<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="portfolioDAOTemplate" class="dao.PortfolioDAOTemplate">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
<bean id="portfolioDAOSupport" class="dao.PortfolioDAOSupport">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
<bean id="myPortfolioService" class="springhibernate.PortfolioService">
<property name="portfolioDAO" ref="portfolioDAOTemplate"></property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributeSource">
<value>springhibernate.PortfolioService.*=PROPAGATION_REQUIRED</value>
</property>
</bean>
<bean id="hibernateInterceptor" class="org.springframework.orm.hibernate3.HibernateInterceptor">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="portfolioService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="myPortfolioService"></property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
<value>hibernateInterceptor</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521/xe" />
<property name="username" value="appUser" />
<property name="password" value="password" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>stockquote.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
applicationContext.xml
Note that the transactionInterceptor and hibernateTransactionInterceptor are declared. While transactionInterceptor is used to set the transaction properties, the hibernateTransactionInterceptor will manage the hibernate transactions.
Schema-based Declarative Transaction Management
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="portfolioDAOTemplate" class="dao.PortfolioDAOTemplate">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
<bean id="portfolioDAOSupport" class="dao.PortfolioDAOSupport">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
<bean id="portfolioService" class="springhibernate.PortfolioService">
<property name="portfolioDAO" ref="portfolioDAOTemplate"></property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<aop:config>
<aop:pointcut id="serviceMethods" expression="execution(* springhibernate.IPortfolioService.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<tx:method name="*" propagation="REQUIRES_NEW" />
</tx:attributes>
</tx:advice>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521/xe" />
<property name="username" value="appUser" />
<property name="password" value="password" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>stockquote.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
applicationContext.xmlUsing Schema-based declarative transaction management is a lot simpler and a lot cleaner. However it is adviced that this method not be used in conjunction with explicit auto-proxying using BeanNameAutoProxyCreator, as it might raise issues like advice not being woven etc. Note that the AOP pointcut is defined to be all methods on the IPortfolioService inteface.
Schema-based Declarative Transaction Management with Annotations
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="portfolioDAOTemplate" class="dao.PortfolioDAOTemplate">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
<bean id="portfolioDAOSupport" class="dao.PortfolioDAOSupport">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
<bean id="portfolioService" class="springhibernate.PortfolioService">
<property name="portfolioDAO" ref="portfolioDAOTemplate"></property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521/xe" />
<property name="username" value="appUser" />
<property name="password" value="password" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>stockquote.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
applicationContext.xmlWhen using annotations, you only have to declare the transaction manager and add <tx:annotation-driven/> to the application context. If the transaction manager is named "transactionManager" then you don't have to declare a transaction-manager attribute for <tx:annotation-driven/> as it happens to be the default value for that attribute.
Also we have to declare the pointcuts in the Service class itself. Note that as annotations are not inherited, declaring the annotations has to be done at the class level.@Transactional
public class PortfolioService implements IPortfolioService {
private PortfolioDAO portfolioDAO;...
Programmatic Transaction Management
For programmatic transaction management in spring, you will need a PlatformTransctionManger in your bean which will be used to create a TransactionTemplate. The TransactionTemplate is used in the same way the HibernateTemplate was used in previous example. Additionally you will have to create a HibernateTransactionManager as shown in the above example.package springhibernate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import beans.StockQuoteBean;
import dao.PortfolioDAO;
public class PortfolioServiceTransaction implements IPortfolioService{
private PortfolioDAO portfolioDAO;
private PlatformTransactionManager transactionManager;
public void getStockQuote(final String id) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
StockQuoteBean result = portfolioDAO.getStockQuote(id);
System.out.println("Symbol in transaction " + result.getStockSymbol());
}
});
}
public void updateStockQuote(final StockQuoteBean stockQuoteBean) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
portfolioDAO.updateStockQuote(stockQuoteBean);
}
});
}
public PortfolioDAO getPortfolioDAO() {
return portfolioDAO;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void setPortfolioDAO(PortfolioDAO portfolioDAO) {
this.portfolioDAO = portfolioDAO;
System.out.println("Setting portfolio DAO to : " + portfolioDAO.getClass());
}
}
PortfolioService.java