Spring事务
事务就是一组逻辑上的动作要全部执行,或者全不执行,是为了保证数据修改前后的数据定义的完整性。
Spring提供两种事务管理方式,分别是编程式和声明式。
- 编程式:通过编码的方式来手动启用,提交或者回滚事务,力度更细,但更麻烦
- 声明式:通过在方法类或者接口上添加注解进行包装,无侵入地实现事务,更方便但是力度更大。
需要注意:使用的数据库必须要支持事务,否则事务将不起作用。比如说Mysql
的MyISAM
引擎就不支持事务。
事务的使用
· 编程式事务
- 编写配置文件
<context:component-scan base-package="transaction"/>
<!--从jdbc.properties中注入datasource-->
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"/>
<property name="username" value="${prop.username}"/>
<property name="url" value="${prop.url}"/>
<property name="password" value="${prop.password}"/>
</bean>
<!--配置事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置Transaction Template-->
<bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!--配置JDBCTemplate-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
- 使用事务
@Component
public class UserService {
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
PlatformTransactionManager transactionManager;
@Autowired
TransactionTemplate transactionTemplate;
//第一种使用方式,显式的去管理
public void transfer(){
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try{
//一组操作............
jdbcTemplate.update("update account set balance = ? where age = ?", 90, 10);
transactionManager.commit(status);
}catch (Exception e){
e.printStackTrace();
transactionManager.rollback(status);
}
}
//使用transactionTemplate
public void transfer2(){
transactionTemplate.execute(new TransactionCallbackWithoutResult(){
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try{
jdbcTemplate.update("update account set balance = ? where age = ?", 100, 10);
}catch (DataAccessException e){
e.printStackTrace();
status.setRollbackOnly();
}
}
});
}
}
·声明式的使用
使用@Transactional
注解加在特定的方法或者接口上。
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
public int save(Info info) throws Exception {
}
几个重要接口
PlatformTransactionManager
:(平台)事务管理器,Spring 事务策略的核心。TransactionDefinition
: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。TransactionStatus
:事务运行状态。
1. PlatformTransactionManager : 事务管理接口
public interface PlatformTransactionManager extends TransactionManager {
//获得事务
//@Param: definition: 事务定义信息
TransactionStatus getTransaction(@Nullable TransactionDefinition definition);
//提交事务
void commit(TransactionStatus status) throws TransactionException;
//回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
2. TransactionDefinition : 事务定义信息
上述获取事务的时候,传入的事务定义信息definition
包含了什么内容呢?
public class DefaultTransactionDefinition{
//设置事务传播行为, 默认是REQUIRED
public final void setPropagationBehavior(int propagationBehavior);
//设置隔离级别, 默认是DEFAULT
public final void setIsolationLevelName(String constantName) ;
//设置超时时间, 如果事务超过该时间限制还未完成,那么就会回滚。 默认为 -1
public final void setTimeout(int timeout) ;
//设置是否只读,默认为false
public final void setReadOnly(boolean readOnly);
//.......................
}
3. TransactionStatus:事务状态接口
public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事务
boolean hasSavepoint(); // 是否有恢复点
void setRollbackOnly(); // 设置为只回滚
boolean isRollbackOnly(); // 是否为只回滚
boolean isCompleted; // 是否已完成
//...........
}
事务传播行为
PROPAGATION_REQUIRED
定义:如果当前没有事务,那么就新建一个事务,如果当前存在事务,那么就加入这个事务。
@Transactional(propagation = Propagation.REQUIRED)
method1(){
//.......
}
@Transactional(propagation = Propagation.REQUIRED)
method2(){
//.......
}
@Transactional(propagation = Propagation.REQUIRED)
method2(){
//.......
}
-
test1-1{ method1(); method2(); }
-
test1-2{ method1(); method3(); }
-
@Transactional(propagation = Propagation.REQUIRED) test1-3{ method1(); method3(); throw new Exception(); }
-
@Transactional(propagation = Propagation.REQUIRED) test1-4{ method1(); try{ method3(); }catch(){ } }
结论:以上试验结果我们证明在外围方法开启事务的情况下Propagation.REQUIRED
修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED
修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
PROPAGATION_REQUIRES_NEW
定义:新建事务,如果当前存在事务,就把当前事务挂起
@Propagation.REQUIRED
method1Re( ){
//.......
}
@PROPAGATION_REQUIRES_NEW
method1Re_new(){
}
@PROPAGATION_REQUIRES_NEW
method2Re_new(){
//.......
}
@PROPAGATION_REQUIRES_NEW
method2Re_new_throw(){
throw Exeception();
}
结论:在外围方法开启事务的情况下Propagation.REQUIRES_NEW
修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。
PROPAGATION_NESTED
定义:新建事务,如果当前存在事务,就在嵌套事务内执行,否则执行和 PROPAGATION_REQUIRED一样的操作
结论:以上试验结果我们证明在外围方法开启事务的情况下Propagation.NESTED
修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务
三者区别
由“1.3 场景”和“3.2 场景”对比,我们可知:NESTED 和 REQUIRED 修饰的内部方法都属于外围方法事务,如果外围方法抛出异常,这两种方法的事务都会被回滚。但是 REQUIRED 是加入外围方法事务,所以和外围事务同属于一个事务,一旦 REQUIRED 事务抛出异常被回滚,外围方法事务也将被回滚。而 NESTED 是外围方法的子事务,有单独的保存点,所以 NESTED 方法抛出异常被回滚而且异常被处理,那么不会影响到外围方法的事务。
由“2.2 场景”和“3.3 场景”对比,我们可知:NESTED 和 REQUIRES_NEW 都可以做到内部方法事务回滚而不影响外围方法事务。但是因为 NESTED 是嵌套事务,所以外围方法回滚之后,作为外围方法事务的子事务也会被回滚。而 REQUIRES_NEW 是通过开启新的事务实现的,内部事务和外围事务是两个事务,外围事务回滚不会影响内部事务。
PROPAGATION_SUPPORTS
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
PROPAGATION_NOT_SUPPORTED
以非事务方式运行,如果当前存在事务,则把当前事务挂起。
PROPAGATION_NEVER
以非事务方式运行,如果当前存在事务,则抛出异常。
Transaction注解使用
/**
* Describes a transaction attribute on an individual method or on a class.
* <p>When this annotation is declared at the class level, it applies as a default
* to all methods of the declaring class and its subclasses.
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
Propagation propagation() default Propagation.REQUIRED; //默认的事务传播
Isolation isolation() default Isolation.DEFAULT; //默认的隔离机制
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; //事务超时时间,默认-1
boolean readOnly() default false; //事务是否只读
Class<? extends Throwable>[] rollbackFor() default {};//事务的回滚异常类型, 默认都进行回滚
}
作用范围:
- 方法 :推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,否则不生效。
- 类 :如果这个注解使用在类上的话,表明该注解对该类及其子类中所有的 public 方法都生效。
- 接口 :不推荐在接口上使用。