对于J2EE 应用程序而言,事务的处理一般有两种模式:
1. 依赖特定事务资源的事务处理
这是应用开发中最常见的模式,即通过特定资源提供的事务机制进行事务管理。如通过JDBC、JTA 的rollback、commit方法;Hibernate Transaction 的rollback、commit方法等。这种方法大家已经相当熟悉。
2. 依赖容器的参数化事务管理
通过容器提供的集约式参数化事务机制,实现事务的外部管理。
Spring提供了一致的事务管理抽象。这个抽象是Spring最重要的抽象之一, 它有如下的优点:
(1)为不同的事务API提供一致的编程模型,如JTA、JDBC、Hibernate、iBATIS数据库层 和JDO
(2)提供比大多数事务API更简单的,易于使用的编程式事务管理API
(3)整合Spring数据访问抽象
(4)支持Spring声明式事务管理
Spring事务管理能给我们带来什么?
对于传统的基于特定事务资源的事务处理而言(如基于JDBC 的数据库访问),Spring并不会对其产生什么影响,我们照样可以成功编写并运行这样的代码。同时,Spring还提供了一些辅助类可供我们选择使用,这些辅助类简化了传统的数据库操作流程,在一定程度上节省了工作量,提高了编码效率。
对于依赖容器的参数化事务管理而言,Spring 则表现出了极大的价值。Spring本身也是一个容器,只是相对EJB容器而言,Spring显得更为轻便小巧。我们无需付出其他方面的代价,即可通过Spring实现基于容器的事务管理(本质上来讲,Spring的事务管理是基于动态AOP)。
(1)编程式事务管理
使用TransactionTemplate
TransactionTemplate采用和其他Spring模板 ,如 HibernateTemplate一样的方法。它使用回调方法,把应用程序代码从处理取得和释放资源中解脱出来(不再有try/catch/finally)。如同 其他模板,TransactionTemplate是线程安全的。
示例:
UserDAO实现类
public class UserDAO implements IUserDAO { private TransactionTemplate transactionTemplate; private HibernateTemplate hibernateTemplate;
public void setSessionFactory( SessionFactory sessionFactory) { this.transactionTemplate = new TransactionTemplate( new HibernateTransactionManager( sessionFactory)); this.hibernateTemplate = new HibernateTemplate(sessionFactory); }
public void insert(User user) { final User userData = user;
transactionTemplate.setPropagationBehavior( TransactionDefinition.PROPAGATION_REQUIRED);
transactionTemplate.execute( new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult( TransactionStatus status) { try { hibernateTemplate.save(userData); } catch(DataAccessException e) { e.printStackTrace(); status.setRollbackOnly(); } } }); }
public User find(Integer id) { User user = (User) hibernateTemplate.get(User.class, id);
return user; } } |
Test测试类
public class Test{ public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext( "beans-config.xml");
IUserDAO userDAO = (IUserDAO) context.getBean("userDAO");
User user = new User(); user.setName("caterpillar"); user.setAge(new Integer(30));
userDAO.insert(user);
user = userDAO.find(new Integer(1));
System.out.println("name: " + user.getName()); } } |
TransactionDefinition
隔离等级 | 描述 |
ISOLATION_DEFAULT | 默认隔离等级 |
ISOLATION_READ_UNCOMMITTED | 最低隔离等级,仅仅保证了读取过程中不会读取到非法数据 |
ISOLATION_READ_COMMITTED | 某些数据库的默认隔离等级;保证了一个事务不会读到另外一个并行事务已修改但未提交的数据 |
ISOLATION_REPEATABLE_READ | 比上一个更加严格的隔离等级。保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据 |
ISOLATION_SERIALIZABLE | 性能代价最为昂贵,最可靠的隔离等级。所有事务都严格隔离,可视为各事务顺序执行 |
传播途径(Propagation Behavior)
Propagation Behavior | 描述 |
PROPAGATION_REQUIRED | 支持现有事务。如果没有则创建一个事务 |
PROPAGATION_SUPPORTS | 支持现有事务。如果没有则以非事务状态运行。 |
PROPAGATION_MANDATORY | 支持现有事务。如果没有则抛出异常。 |
PROPAGATION_REQUIRES_NEW | 总是发起一个新事务。如果当前已存在一个事务,则将其挂起。 |
PROPAGATION_NOT_SUPPORTED | 不支持事务,总是以非事务状态运行,如果当前存在一个事务,则将其挂起。 |
PROPAGATION_NEVER | 不支持事务,总是以非事务状态运行,如果当前存在一个事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前已经存在一个事务,则以嵌套事务的方式运行,如果当前没有事务,则以默认方式(第一个)执行 |
(2)声明式事务管理
Spring也提供了声明式事务管理。这是通过Spring AOP实现的。
大多数Spring用户选择声明式事务管理。这是最少影响应用代码的选择, 因而这是和非侵入性的轻量级容器的观念是一致的。
示例:
Spring配置文件
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/test</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>root</value> </property> </bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" destroy-method="close"> <property name="dataSource"> <ref bean="dataSource"/> </property> <property name="mappingResources"> <list> <value>com/pojo/User.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </prop> </props> </property> </bean>
<bean id="userDAO" class="com.dao.UserDAO"> <property name="sessionFactory"> <ref bean="sessionFactory"/> </property> </bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref bean="sessionFactory"/> </property> </bean>
<bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref bean="transactionManager"/> </property> <property name="proxyInterfaces"> <list> <value>com.dao.IUserDAO</value> </list> </property> <property name="target"> <ref bean="userDAO"/> </property> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> |
UserDAO实现类
public class UserDAO implements IUserDAO { private HibernateTemplate hibernateTemplate;
public void setSessionFactory( SessionFactory sessionFactory) { hibernateTemplate = new HibernateTemplate(sessionFactory); }
public void insert(User user) { hibernateTemplate.save(user); }
public User find(Integer id) { User user = (User) hibernateTemplate.get(User.class, id);
return user; } } |
Test测试类
public class Test{ public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext( "beans-config.xml");
IUserDAO userDAO = (IUserDAO) context.getBean("userDAOProxy"); User user = new User(); user.setName("cater"); user.setAge(new Integer(30));
userDAO.insert(user);
user = userDAO.find(new Integer(1));
System.out.println("name: " + user.getName()); } } |
配置中包含了dataSource,transactionManager 等资源定义。这些资源都为一个名为userDAOProxy 的TransactionProxyFactoryBean 服务, 而userDAOProxy 则对包含实际数据逻辑的userDAO进行了事务性封装。
可以看到,在userDAOProxy 的"transactionAttributes"属性中,我们定义了针对userDAO 的事务策略,即将所有名称以insert 开始的方法(如UserDAO.insertUser方法)纳入事务管理范围。如果此方法中抛出异常,则Spring将当前事务回滚,如果方法正常结束,则提交事务。
而对所有名称以get 开始的方法(如UserDAO.getUser 方法)则以只读的事务处理机制进行处理。(设为只读型事务(readOnly),可以使持久层尝试对数据操作进行优化,如对于只读事务Hibernate将不执行flush操作,而某些数据库连接池和JDBC 驱动也对只读型操作进行了特别优化)。