springboot事务详解
阅读数:115 评论数:0
跳转到新版页面分类
python/Java
正文
一、概述
1、事务
事务就是一组操作数据库的动作集合,集合中的动作要么全部执行,要么全部不执行。
Atomicity 原子性 | 整个事务是一个整体,是不可分割的最小工作单位。一个事务中的所有操作要么全部执行成功,要么全部都不执行,其中任何一条操作失败,都会导致事务回滚 |
Consistency一致性 | 数据的记录总是从一个一致状态转变成另一个致性状态。 |
Isolation 隔离性 | 一个事务的执行,不受其它事务的干扰,即并发执行的事务之间互不干扰 |
Duration 持久性 | 数据一旦提交,结果就是永久性的,并不应为宕机等情况丢失。 |
二、事务的实现方式
(1)原子性和持久性利用redo log(重做日志)实现。
(2)一致性利用undo log(回滚日志)实现。
(3)隔离性利用锁来实现。
(4)innodb引擎支持事务,myisam不支持事务
spring事务管理分为编码式和声明式两种:
使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。
PlatformTransactionManager接口中定义了创建、提交和回滚事务的方法:
@Autowired
DataSourceTransactionManager dataSourceTransactionManager;
@Autowired
TransactionDefinition transactionDefinition;
// 手动创建事务
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
// 手动提交事务
dataSourceTransactionManager.commit(transactionStatus);
// 手动回滚事务。(最好是放在catch 里面,防止程序异常而事务一直卡在哪里未提交)
dataSourceTransactionManager.rollback(transactionStatus);
常用的实现类比如DataSourceTransactionalManager,它对应的是传统的JDBC事务管理。
对方法前后进行拦截,在目标方法之前创建或者加入一个事务,方法执行之后根据执行情况提交或回滚事务。声明式事务有两种方式,一种是在配置文件中做相关的事务规则声明,另一种是基于@Transaction注解的方式。
三、@Transactional注解
(1)在具体的类(或类的方法)上使用@Transactional注解,而不要使用在类所要实现的任何接口上。
(2)只能被应用在public修饰的方法上。
(3)基于动态代理,需要一个类调用另一个类,类内调用会失效。
(4)假设被外部调用的公共方法A有两个进行数据操作的方法B和方法C:
A声明了事务 | 正常运行,事务由A控制 |
A未声明事务 | B和C是其它类的方法,且各种声明事务,事务由B和C各种控制 |
B和C是本类的方法,不论B和C是否声明事务,事务都不会生效 |
(5)如果自已使用的try-catch捕获异常,则回滚不会执行,如果需要try-catch后仍回滚,需要
//手动抛出异常
try{
....
}catch(Exception e){
logger.error("fail",e);
throw new RuntimeException;
}
// 或者手动回滚
try{
...
}catch(Exception e){
log.error("fail",e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return false;
}
参 数 名 称 | 功 能 描 述 |
---|---|
readOnly | 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) |
rollbackFor | rollbackFor 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class,Exception.class}) |
rollbackForClassName | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:指定单一异常类名称@Transactional(rollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
noRollbackFor | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName | 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(noRollbackForClassName={"RuntimeException", "Exception"}) |
propagation | 该属性用于设置事务的传播行为。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly=true) |
isolation | 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 |
timeout | 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 事物超时设置:@Transactional(timeout=30) ,设置为30秒 |
(1)默认情况下,Spring会对unchecked异常进行事务回滚,也就是默认对RuntimeException异常或者其子类进行事务回滚;如果是checked异常则不会回滚。
例如空指针异常、算数异常都会回滚;但文件读写、网络等异常,则不会回滚。所以若想对所有异常都起作用,需要:
@Transactional(rollbackFor = Exception.class)
Spring在TranscationDefinition接口中规定了7种类型的事务传播行为,Propagation枚举引用了这些类型,开发过程中我们一般直接使用Propagation枚举。
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 需要事务(默认)。若当前无事务,新建一个事务;若当前有事务,加入此事务中 |
PROPAGATION_SUPPORTS | 支持事务。若当前没有事务以非事务方式执行;若当前有事务,加入此事务中 |
PROPAGATION_MANDATORY | 强制使用事务。若当前有事务,就使用当前事务;若当前没有事务,抛出IllegalTransactionStateException异常 |
PROPAGATION_REQUIRES_NEW | 新建事务。无论当前是否有事务,都新建事务运行 |
PROPAGATION_NOT_SUPPORTED | 不支持事务。若当前存在事务,把当前事务挂起,然后运行方法 |
PROPAGATION_NEVER | 不使用事务。若当前方法存在事务,则抛出IllegalTransactionStateException异常,否则继续使用无事务机制运行 |
PROPAGATION_NESTED | 嵌套。如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作 |
@Transactional(isolation = Isolation.READ_COMMITTED)
隔离级别 | 含义 |
---|---|
DEFAULT | 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别另外四个与JDBC的隔离级别相对应 |
READ_UNCOMMITTED | 最低的隔离级别。事实上我们不应该称其为隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。可能导致脏,幻,不可重复读 |
READ_COMMITTED | 大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾的是,在该事务提交后,你就可以查看其他事务插入或更新的数据。这意味着在事务的不同点上,如果其他事务修改了数据,你就会看到不同的数据。可防止脏读,但幻读和不可重复读仍可以发生 |
REPEATABLE_READ | 比ISOLATION_READ_COMMITTED更严格,该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。可防止脏读,不可重复读,但幻读仍可能发生 |
SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。代价最大、可靠性最高的隔离级别,所有的事务都是按顺序一个接一个地执行。避免所有不安全读取 |
四、常用方式
@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder() throws Exception {
success();
//假如exception这个操作数据库的方法会抛出异常,方法success()对数据库的操作会回滚
exception();
return ApiReturnUtil.success();
}
@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder (){
success();
try {
exception();
} catch (Exception e) {
e.printStackTrace();
//手工回滚异常
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return ApiReturnUtil.error();
}
return ApiReturnUtil.success();
}
@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder (){
success();
//只回滚以下异常,设置回滚点
Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
try {
exception();
} catch (Exception e) {
e.printStackTrace();
//手工回滚异常,回滚到savePoint
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
return ApiReturnUtil.error();
}
return ApiReturnUtil.success();
}