一、什么是Spring事务
在Spring应用中,提到的事务通常是指对数据库的事务操作,遵循数据库事务的ACID原则。
二、Spring事务的特性
1、事务的传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
- propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是Spring默认的选择。
- propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
- propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。
2、事务的隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。
- TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
3、只读属性
事务的第三个特性是它是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。
4、事务超时
因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
5、回滚规则
指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。
三、spring怎么管理事务
Spring并不直接管理事务,而是提供了多种事务管理器。Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器。接口定义如下:
public interface PlatformTransactionManager {
//根据事务定义TransactionDefinition,获取事务
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
//提交事务
void commit(TransactionStatus status) throws TransactionException;
//回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
其中,TransactionDefinition是事务的一些基础信息,如超时时间,隔离级别,传播特性等;TransactionStatus是具体的事务,包括事务状态,如是不是一个新事务,是否被标记为回滚等。Spring针对不同的ORM提供的事务管理器,都是集成自AbstractPlatformTransactionManager,而AbstractPlatformTransactionManager又实现了PlatformTransactionManager。
2.1 JDBC、Mybatis以及ibatis事务管理器
如果应用程序中直接使用JDBC来进行持久化,需要配置JDBC事务管理器DataSourceTransactionManager,通过DataSource获取到java.sql.Connection来管理事务,调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
2.2 Hibernate事务管理器
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
2.3 JPA事务管理器
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
通过entityManagerFactory属性指定需要事务管理的javax.persistence.EntityManagerFactory对象。还需要为entityManagerFactory对象指定jpaDialect属性,该属性所对应的对象指定了如何获取连接对象、开启事务、关闭事务等事务管理相关的行为。
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
……
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
2.4 Java分布式事务管理器JTA
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransaction" ref="jotm" />
</bean>
<!-- JOTM实例 -->
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean">
<property name="defaultTimeout" value="500000"/>
</bean>
分布式事务通常是指多个数据源之间的实物,在tomcat下,是没有分布式事务的,不过可以借助于第三方软件jotm(Java Open Transaction Manager )和AtomikosTransactionsEssentials实现。
2.5 JDO事务管理器
<bean id="txManager" class="org.springframework.orm.jdo.JdoTransactionManager">
<property name="persistenceManagerFactory" ref="persistenceManagerFactory"/>
</bean>
通过persistenceManagerFactory属性指定需要事务管理的javax.jdo.PersistenceManagerFactory对象。
具体可参考:http://hhhk.iteye.com/blog/2082714
四、Spring事务使用
Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。
1、编程式事务
Spring提供两种方式的编程式事务管理,分别是:使用TransactionTemplate和直接使用PlatformTransactionManager。
1)采用TransactionTemplate和采用其他Spring模板,如JdbcTempalte和HibernateTemplate是一样的方法,它使用回调方法,把应用程序从处理取得和释放资源中解脱出来。TransactionTemplate是线程安全的。
<!-- JDBC事务管理器 注意:事务管理器传的参数是数据源-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager" scope="singleton">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<!-- 声明事务模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
</bean>
// 事务模板 execute 执行方法实现
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}else {
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
// 执行事务
result = action.doInTransaction(status);
}catch (RuntimeException ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}catch (Error err) {
// Transactional code threw error -> rollback
rollbackOnException(status, err);
throw err;
}catch (Exception ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
// 如果事务未抛异常,提交事务
this.transactionManager.commit(status);
return result;
}
}
所以,实际应用中使用模板的方式为:
// 新建一个TransactionTemplate
TransactionTemplate tt = new TransactionTemplate();
Object result = tt.execute(new TransactionCallback(){
public Object doTransaction(TransactionStatus status){
// 执行逻辑,并返回执行结果
return resultOfUpdateOperation();
}
}); // 执行execute方法进行事务管理
2、声明式事务管理
1)使用TransactionProxyFactoyBean方式声明事务
// 定义数据源
@Bean(name = "dataSource")
public org.apache.commons.dbcp2.BasicDataSource dataSource(){
org.apache.commons.dbcp2.BasicDataSource dataSource = new org.apache.commons.dbcp2.BasicDataSource();
dataSource.setInitialSize(10);
dataSource.setMaxIdle(10);
dataSource.setMinIdle(5);
dataSource.setMaxWaitMillis(100000);
dataSource.setValidationQuery("SELECT 1");
dataSource.setConnectionProperties("characterEncoding=utf8");
dataSource.setTimeBetweenEvictionRunsMillis(3000000L);
dataSource.setMinEvictableIdleTimeMillis(1800000L);
dataSource.setTestWhileIdle(true);
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
// 定义事务管理器
@Bean
public DataSourceTransactionManager transactionManager(){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
return transactionManager;
}
// 定义事务的执行对象
@Bean
public TransactionProxyFactoryBean transactionProxyFactoryBean(){
TransactionProxyFactoryBean serviceProxy = new TransactionProxyFactoryBean();
serviceProxy.setTransactionManager(transactionManager());
// 设置需要事务的bean
serviceProxy.setProxyInterfaces(new Class[]{PayRecordService.class});
// 设置bean中哪些方法要什么样的隔离级别
Properties props = new Properties();
props.setProperty("add*", "ISOLATION_DEFAULT,PROPAGATION_REQUIRED");
props.setProperty("save","ISOLATION_DEFAULT,PROPAGATION_REQUIRED");
serviceProxy.setTransactionAttributes(props);
return serviceProxy;
}
2)Aspect Aop的事务实现
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="interceptorPointCuts" expression="execution(* com.bluesky.spring.dao.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />
</aop:config>
@Aspect
@Configuration
public class AopAspect {
@Resource
private PlatformTransactionManager platformTransactionManager;
@Bean
public TransactionInterceptor txAdvice(){
DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();
txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
txAttr_REQUIRED_READONLY.setReadOnly(true);
// 根据名字匹配
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
source.addTransactionalMethod("save*", txAttr_REQUIRED);
source.addTransactionalMethod("delete*", txAttr_REQUIRED);
source.addTransactionalMethod("update*", txAttr_REQUIRED);
source.addTransactionalMethod("set*", txAttr_REQUIRED);
source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
return new TransactionInterceptor(platformTransactionManager, source);
}
@Bean
public Advisor txAdvisor(){
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* *..service.*.*(..))");
return new DefaultPointcutAdvisor(pointcut, txAdvice());
}
}
3)基于@Transactional注解的声明式事务
<!-- 注解方式配置事物 -->
<tx:annotation-driven transaction-manager="transactionManager" />
4)使用拦截器
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Dao</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>