目录
-
-
- 一、Spring事务分类
- 二、Spring事务的三大接口
-
-
- 2.1 PlatformTransationManager接口
- 2.2 TransactionDefinition接口
- 2.3 TransactionStatus接口
-
- 三、Spring事务嵌套最佳实践
-
-
- 3.1 最佳实践场景一:
- 3.2 最佳实践场景二:
- 3.3 最佳实践场景三:
- 3.4 最佳实践场景四:
- 3.5 最佳实践场景五:
- 3.6 最佳实践场景六:
- 3.7最佳实践场景七:
-
- 四、Spring事务失效的场景
-
一、Spring事务分类
- 编程式事务: 如果系统需要明确的事务,并且需要细粒度的控制各个事务的边界,此时建议使用编程式事务
- 声明式事务: 如果系统对于事务的控制粒度较为粗糙,则建议使用声明式事务,可以通过注解@Transational实现,原理是利用Spring框架通过AOP代理自动完成开启事务,提交事务,回滚事务。回滚的异常默认是运行时异常,可以通过rollbackFor属性制定回滚的异常类型。
二、Spring事务的三大接口
提示:通过Spring事务的源码可知,Spring不是直接管理事务的,而是提供了多种事务管理器,通过这些事务管理器,Spring将事务委托给了Hibernate、MyBaits、JTA等持久性框架的事务来实现
2.1 PlatformTransationManager接口
public interface PlatformTransactionManager extends TransactionManager {
/**
* 获取事务状态
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
/**
* 提交事务
*/
void commit(TransactionStatus var1) throws TransactionException;
/**
* 回滚事务
*/
void rollback(TransactionStatus var1) throws TransactionException;
}
2.2 TransactionDefinition接口
public interface TransactionDefinition {
/**
* 支持当前事务,若当前没有事务就创建一个新的事务
*/
int PROPAGATION_REQUIRED = 0;
/**
* 如果当前存在事务,则加入该事务,如果当前没有事务,则以非事务的方式继续运行
*/
int PROPAGATION_SUPPORTS = 1;
/**
* (强制性的)
* 如果当前存在事务,则加入该事务,如果当前没有事务,则抛出异常
*/
int PROPAGATION_MANDATORY = 2;
/**
* 创建一个新的事务,如果当前存在事务,则把当前事务挂起
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* 以非事务方式运行,如果当前存在事务,则把当前事务挂起
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* 以非事务的方式运行,如果当前存在事务,则抛出异常
*/
int PROPAGATION_NEVER = 5;
/**
* (嵌套的)
* 表示如果当前正有一个事务在运行中,则该方法运行在一个嵌套的事务中,被嵌套的事务可以独立于封装的事务进行提交或者回滚(这里需要事务的保存点),如果封装的事务不存在,后续事务行为同PROPAGATION_REQUIRES_NEW
*/
int PROPAGATION_NESTED = 6;
/**
* 使用后端的默认隔离级别
*/
int ISOLATION_DEFAULT = -1;
/**
* 读未提交
*/
int ISOLATION_READ_UNCOMMITTED = 1;
/**
* 读已提交
*/
int ISOLATION_READ_COMMITTED = 2;
/**
* 可重复度
*/
int ISOLATION_REPEATABLE_READ = 4;
/**
* 串行化
*/
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;
default int getPropagationBehavior() {
return 0;
}
default int getIsolationLevel() {
return -1;
}
default int getTimeout() {
return -1;
}
default boolean isReadOnly() {
return false;
}
@Nullable
default String getName() {
return null;
}
static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
}
2.3 TransactionStatus接口
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
/**
* 是否有保存点
*/
boolean hasSavepoint();
/**
* 将事务涉及的数据刷新到磁盘
*/
void flush();
}
三、Spring事务嵌套最佳实践
我们将此方法作为内部方法,有外部方法调用此方法,我们在方法的最后面故意制造了算术异常来检验以下场景:
/**
* 项目延期
*
* @param id 工单id
* @param value 延期天数
* @return
*/
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public Boolean projectDelay(String id, Integer value) {
EqOverhaulWorkOrderEntity eqOverhaulWorkOrderEntity = eqOverhaulWorkOrderMapper.selectById(id);
Date startTime = eqOverhaulWorkOrderEntity.getStartTime();
Calendar calendar = Calendar.getInstance();
calendar.setTime(startTime);
calendar.add(Calendar.DAY_OF_MONTH, value);
eqOverhaulWorkOrderEntity.setStartTime(calendar.getTime());
int update = eqOverhaulWorkOrderMapper.updateById(eqOverhaulWorkOrderEntity);
int i = 1/0;
return update == 1;
}
3.1 最佳实践场景一:
场景一为外部方法无事务注解,内部方法添加REQUIRED事务传播类型:
总结: 内部方法抛出异常,内部方法执行失败,不会影响外部方法的执行,外部方法执行成功。
3.2 最佳实践场景二:
场景二为外部方法添加REQUIRED事务传播类型,内部方法无事务注解。
总结: 内部方法抛出异常,会影响外部方法的执行,导致外部事务的回滚。
3.3 最佳实践场景三:
场景三为外部方法添加REQUIRED事务传播类型,内部方法添加REQUIRED事务传播类型。
总结: 内部方法抛出异常,会影响外部方法的执行,事务回滚。
3.4 最佳实践场景四:
场景四为外部方法添加REQUIRED事务传播类型,内部方法添加NOT_SUPPORTED事务传播类型。
总结: 内部方法抛出异常,外部方法回滚,内部方法不会回滚,外部方法抛出异常,内部方法执行成功,内部方法提交,内部方法抛出异常,也不会回滚。内部方法永远不会回滚。
3.5 最佳实践场景五:
场景五外部方法添加REQUIRED事务传播类型,内部方法添加REQUIRED_NEW事务传播类型。
总结:内部方法抛出异常,内部方法和外部方法都会执行失败,事务回滚。
3.6 最佳实践场景六:
场景六为外部方法添加REQUIRED事务传播类型,内部方法添加REQUIRED_NEW事务传播类型,并且把异常代码移动到外部方法的尾部。
总结: 外部方法抛出异常,外部方法执行失败,事务回滚,内部方法执行成功,内部方法事务提交。
3.7最佳实践场景七:
场景七为外部方法添加REQUIRED,内部方法添加REQUIRED_NEW事务传播类型,并且把异常代码移动到外部方法的尾部,同时外部方法和内部方法在同一个类中。
总结: 外部方法抛出异常, 内部方法和外部方法都会回滚。
四、Spring事务失效的场景
- 数据库不支持事务
- 事务方法未被Spring管理,也就是事务方法所在的类没有加载到Spring IOC容器中
- 方法没有被public修饰
@Override
@Transactional(rollbackFor = Exception.class)
private Boolean projectDelay(String id, Integer value) {
EqOverhaulWorkOrderEntity eqOverhaulWorkOrderEntity = eqOverhaulWorkOrderMapper.selectById(id);
Date startTime = eqOverhaulWorkOrderEntity.getStartTime();
Calendar calendar = Calendar.getInstance();
calendar.setTime(startTime);
calendar.add(Calendar.DAY_OF_MONTH, value);
eqOverhaulWorkOrderEntity.setStartTime(calendar.getTime());
int update = eqOverhaulWorkOrderMapper.updateById(eqOverhaulWorkOrderEntity);
return update == 1;
}
因为方法是私有的,加在私有方法上的事务注解不生效。
4. 在同一个类中的方法调用。
提示:举例详情见最佳实践场景七
5. 方法的事务传播类型不支持事务。
提示:举例详情见最佳实践场景四
6. 不正确地捕获异常。
使用了try-catch代码块将异常捕捉了,没有向上抛出异常,事务不会回滚。
7.标注错误的异常类型。
Spring事务默认回滚类型是RuntimeException类型,如果没有制定回滚的类型,抛出的错误不是RuntimeException类型,则无法回滚