Spring 对于事务管理的支持
关于事务管理的基本概念,查看
http://blog.csdn.net/al_assad/article/details/78808820
JDBC 底层已经提供了对于事务管理的一系列接口和实现类,Spring 对于 JDBC 的事务管理进行了进一步的封装,不仅支持编程式的事务管理,还提供了更加简洁的声明式事务管理;
Spring 事务管理的和核心接口
Spring 事务管理 SPI(Service Provider Interface)的抽象接口主要包括3个核心接口,它们位于 org.springframework.transaction 包中,如下:
- TransactionDefinition :定义了 Spring 兼容的事务属性,这些属性对事务管理控制的若干方面进行配置 ;
- PlatformTransactionManager:事务管理器,管理事务的行为,如创建,提交,回滚等;
- TransactionStatus:代表一个事务的具体运行状态,事务管理器可以以通过该接口获取事务运行期间的状态信息,也可以通过该接口间接的回滚事务;
Spring 事务管理器实现类
Spring 将事务管理的实现细节委托底层具体的持久化实现框架来实现,为不同的持久化框架提供了 PlatformTransactionManager 接口的实现类:
org.springframework.jdbc.dataSource.TransactionManager | 使用 Spring JDBC 或 MyBatis 等基于 DataSource 数据源的持久化技术 |
org.springframework.orm.hibernateX.HibernateTransactionManager | 使用 Hibernnate (3.0+)的持久化技术 |
org.springframework.orm.jpa.JpaTransactionManager | 使用 JPA 的持久化技术 |
org.springframework.orm.jdo.JdoTransactionManager | 使用 JDO 的持久化技术 |
org.springframework.transaction.jta.JtaTransactionManager | 具有多个数据源的全局事务使用该事务管理器 |
在使用 Spring 进行事务管理时,首先要对事务管理器进行配置,常用配置示例如下:
1)Spring JDBC 和 MyBatis
<!--数据源配置-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="#{'jdbc.driverClassName'}"
p:url="#{'jdbc.url'}"
p:username="#{'jdbc.username'}"
p:password="#{'jdbc.password'}" />
.....
<!--事务管理器配置-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
2)Hibernate
<!--数据源配置,同上-->
<!--配置会话工厂-->
<bean id="sessionFactory" class="org.springframework.hibernate4.LocalSessionFactoryBean"
p:dataSource-ref="dataSoucre"
p:mappingResource="classpath:hibernate.hbm.xml">
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">#{'hibernate.dialect'}</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
</props>
</property>
</bean>
<!--事务管理器配置-->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
3)JPA
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSoucre" />
<!--事务管理器配置-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
4)JTA
<!--通过 jee 命名空间获取 Java EE 应用服务器容器中的数据源 -->
<jee:jndi-lookup id="accountDs" jndi-name="java:comp/env/jdbc/account" />
<jee:jndi-lookup id="orderDs" jndi-name="java:comp/env/jdbc/order" />
<!--事务管理器配置-->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
事务同步管理器
Spring 将 JDBC 的 Connection、Hibernate 的 Session 等访问数据的连接或会话对象统称为资源,这些资源在同一时刻是不能多线程共享的,为了让 DAO,Service 类能做到 singleton,Spring 的事务同步管理类 org.springframework.transaction.support.TransactionSynchronizedManager 使用
ThreadLocal类作为基础,为不同的事务线程提供了独立的资源副本;
Spring 为不同的持久化技术提供从
TransactionSynchronizedManager 获取对应线程绑定资源的工具类,如下:
org.springframework.jdbc.dataSource.DataSourceUtils | Spring JDBC 或 Mybatis |
org.springframework.orm.hibernateX.SessionFactoryUtils | Hibernate(3.0+) |
org.springframework.orm.jpa.EntityManagerFactoryUtils | JPA |
org.springframework.orm.jdo.PersistenceManagerFactoryUtils | JDO |
这些工具类都提供了可以获取资源的静态方法,如:
DataSourceUtils.getConnection(DataSoucre dataSoucre):从指定的数据源中获取绑定的Connection;
SessionFactory.getSession(
DataSoucre dataSoucre):Hibernate框架中 从指定的数据源中获取把绑定的Session;
事务传播行为
Spring 通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口的方法中,在 TransactionDefinition 接口中规定了7种类型的事务传播行为,他们规定了事务方法之间发生嵌套时事务如何传播;
PROPAGATION_REQUIRED | 默认设置,如果当前没有事务,这新建一个事务;如果已经存在一个事务,这加入到该事务中; |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,则以非事务的方式执行; |
PROPAGATION_MANDATORY | 使用当前事务,如果当前没有事务,则抛出异常; |
PROPAGATION_REQUIRED_NEW | 新建事务,如果当前存在事务,则将该事务挂起; |
PROPAGATION_NOT_SUPPORTED | 以非事务的形式执行,如果当前存在事务,则将事务挂起; |
PROPAGATION_NEVER | 以非事务的形式执行,如果当前存在事务,则抛出异常; |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则执行类似于 PROPAGATION_REQUIRED的操作 |
Spring 声明式事务的配置
Spring 对于事务的配置方式,除了可以通过 org.springframework.transaction.support.TrasacntionTempalte 使用编程式配置之外,还可以使用声明式进行配置,包括使用XML和注解进行声明式配置;
以下是一个需要被实施事务增强的 service 接口 UserService :
package site.assad.service_tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import site.assad.dao.GameDao;
import site.assad.dao.UserDao;
import site.assad.domain.Game;
import site.assad.domain.User;
public class UserService {
private UserDao userDao;
private GameDao gameDao;
public User getUser(final int userid){
User user = null;
user = userDao.getUserById(userid);
return user;
}
public void collect(final int userid,final int gameid){
User user = new User(userid);
Game game = new Game(gameid);
userDao.collectGame(user,game);
gameDao.gameCollected(game,user);
}
//public void addUser(..); 省略
//public void updateUser(..); 省略
}
使用XML配置声明式事务
在Spring 2.0 之前,由于没有引入AOP,只能通过TransactionProxyFactoryBean 代理工厂类进行xml方式的配置,该过程比较繁琐,在Spring 2.0 开始,Spring 引入AOP ,同时提供了使用基于 aop/tx 命名空间的配置(实际开发中更多地使用这种简化的配置方式);
示例代码模块:
site/assad/domain/*(领域对象)
site/assad/dao/*(dao层对象)
site/assad/service_tx/* (service层对象)
site/assad/applicationContext-tx.xml(配置文件)
site/assad/service_tx/*(测试文件)
使用基于aop/tx 命名空间的xml配置文件,对以上 UserService 等织入事务增强切面,代码块如下:
<!--使用aop/tx 的 xml 配置方式配置声明式事务-->
<beans ...>
<!--扫描bean-->
<context:component-scan base-package="site.assad.dao" />
<context:component-scan base-package="site.assad.service_tx" />
<!--属性占位符配置-->
<context:component-scan base-package="site.assad" />
<context:property-placeholder location="classpath:site/assad/jdbc.properties" />
<!--数据源配置-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="#{'jdbc.driverClassName'}"
p:url="#{'jdbc.url'}"
p:username="#{'jdbc.username'}"
p:password="#{'jdbc.password'}" />
<!--Jdbc模板配置-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource" />
<!--事务管理器配置-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
<!--使用tx命名空间 配置事务增强-->
<tx:advice id="txActive" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="false" />
<tx:method name="add*" rollback-for="Exception" />
<tx:method name="update*" />
<tx:method name="collect" />
</tx:attributes>
</tx:advice>
<!--使用 schema 的方式配置 事务增强的 aop 切面-->
<aop:config proxy-target-class="true">
<!--定义aop切点-->
<aop:pointcut id="serviceMethod" expression="execution(* site.assad.service_tx.*Service.*(..))" />
<!--织入事务增强-->
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txActive" />
</aop:config>
</beans>
这种配置方式可以在 Service 类不需要侵入任何代码的情况下,完成事务增强切面的织入;
其中配置 <tx:active>事务增强切面中
<tx:method > 元素常用的属性如下:
属性 | 默认值 | 说明 |
name | 无 | 制定方法切点,如:“get*”,“collect”,“on*Event”等 |
propagation | REQUIRED | 指定事务传播行为,候选值为: REQUIRED、SUPPORTS、MANDATORY、REQUIRED_NEW、NOT_SUPPORTED、NEVER、NESTED |
isolation | DEFAULT | 事务隔离级别,候选值为: DEFAULT、READ_UNCOMMITED、READ_COMMIT、REPEATABLE_READ、SERIALIZABLE 见:事务管理 |
timeout | -1 | 事务超时时间(单位秒) |
read-only | false | 事务是否只读 |
rollback-for | 所有运行期异常回滚 | 触发事务回滚的 Exception,用异常名称片段进行匹配,可以设置多个,中间用 “,” 隔开 |
no-rollback-for | 所有检查异常类型不回滚 | 不触发事务回滚的 Exception; |
其中除了 name 属性为必要属性外,其他属性都可选用;
使用@Transactional注解配置声明式事务
Spring 的声明式事务,除了支持 XML 进行配置,还支持使用 @Transactional 注解进行配置,@Transactional 注解可以使用在接口定义/接口方法、类定义、类的public方法上;
Spring 建议在业务实现类上使用 @Transactional 注解,不推荐在接口定义/接口方法上使用,这是由于注解无法继承,在继承实现接口时,可能会留下一些容易被忽略的隐患;
示例代码模块:
site/assad/domain/*(领域对象)
site/assad/dao/*(dao层对象)
site/assad/service_anno/* (service层对象)
site/assad/applicationContext-anno.xml(配置文件)
site/assad/service_anno/*(测试文件)
以下是 UserDao 使用了注解进行事务管理切点的标注:
package site.assad.service_tx;
//在类级别上标注事务增强切点
public class UserService {
private UserDao userDao;
private GameDao gameDao;
public User getUser(final int userid){
User user = null;
user = userDao.getUserById(userid);
return user;
}
public void collect(final int userid,final int gameid){
User user = new User(userid);
Game game = new Game(gameid);
userDao.collectGame(user,game);
gameDao.gameCollected(game,user);
}
//public void addUser(..); 省略
//public void updateUser(..); 省略
}
@Transactional 注解常用的属性如下:
属性 | 默认值 | 说明 |
propagation | REQUIRED | 指定事务传播行为,候选值为: REQUIRED、SUPPORTS、MANDATORY、REQUIRED_NEW、NOT_SUPPORTED、NEVER、NESTED 如:@Transactional(propagation=Propagation.REQUIRED) |
isolation | DEFAULT | 事务隔离级别,候选值为: DEFAULT、READ_UNCOMMITED、READ_COMMIT、REPEATABLE_READ、SERIALIZABLE 见:03. 事务管理 如:@Transactional(isolation=Isolation.READ_COMMIT) |
timeout | -1 | 事务超时时间(单位秒) 如:@Transactional(timeout=10) |
readOnly | false | 事务是否只读 如:@Transactional(readOnly=true) |
rollbackFor | 所有运行期异常回滚 | 触发事务回滚的 Exception,类型为Class<T> 如:@Transactional(rollbackFor={SQLException.class}) |
rollbackForClass | 同上 | 如:@Transactional(rollbackForClass={"SQLException"}) |
noRollbackFor | 所有检查异常类型不回滚 | 不触发事务回滚的 Exception; |
noRollbackForClass | 同上 | 同上 |
在使用 @Transactional 注解后,需要在配置文件中配置注解织入驱动,如下:
<beans ...>
<!--扫描bean-->
<context:component-scan base-package="site.assad.dao" />
<context:component-scan base-package="site.assad.service_tx" />
<!--属性占位符配置-->
<context:component-scan base-package="site.assad" />
<context:property-placeholder location="classpath:site/assad/jdbc.properties" />
<!--数据源配置-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="#{'jdbc.driverClassName'}"
p:url="#{'jdbc.url'}"
p:username="#{'jdbc.username'}"
p:password="#{'jdbc.password'}"/>
<!--Jdbc模板配置-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource" />
<!--事务管理器配置-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
<!--对标注 @Transactional 的 bean 织入事务增强-->
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
</beans>