注意事项:
@Transactional( rollbackFor={Exception.class,其它异常})
.如果让unchecked例外不回滚:@Transactional(notRollbackFor=RunTimeException.class)
@SpringBootApplication
@EnableTransactionManagement
public class StartSpringBootMain {
public static void main(String[] args) throws Exception {
SpringApplication.run(StartSpringBootMain.class, args);
}
}
@Service
@Transactional(readOnly = true,rollbackFor = Exception.class)
public class UserService {
@Autowired(required = false)
private UserMapper userMapper;
@Transactional(readOnly = false,rollbackFor = Exception.class)
public Integer updates(User blues,User zhangsan) throws Exception{
int num = userMapper.updateById(blues);
System.out.println(1/0);
num = num + userMapper.updateById(zhangsan);
return num;
}
}
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)-(required)
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
methodB();
// do something
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
// do something
}
//=========分析==============//**
1、单独调用methodB时,因为上下文不存在事务,会开启一个新事物
2、调用methodA时,因为不存在事务,会开启一个事务,
当执行到methodB方法时,有事务会将事务加入到当前的事务中来
**//=======================
容器不为这个方法开启事务(not_supported)
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
methodB();
// do something
}
// 事务属性为SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
// do something
}
//=========分析==============/**
1、单独调用methodB时,是非事务执行
2、调用methodA时,因为不存在事务,会开启一个事务,
当执行到methodB方法时,有事务会将事务加入到当前的事务中来
**//=======================//
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务(requires_new)
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
doSomeThingA();
methodB();
doSomeThingB();
// do something else
}
// 事务属性为REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// do something
}
//=========分析==============/**
1、单独调用methodB时,因为当前没有事务,会抛出异常
2、调用methodA时,因为不存在事务,会开启一个事务,
当执行到methodB方法时,有事务会将事务加入到当前的事务中来
**//=======================//
必须在一个已有的事务中执行,否则抛出异常(mandatory)
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
methodB();
// do something
}
// 事务属性为MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
// do something
}
//=========分析==============/**
1、单独调用methodB时,因为当前没有事务,会抛出异常
2、调用methodA时,因为不存在事务,会开启一个事务,
当执行到methodB方法时,会将A的事务挂起来,新建一个事务B,执行完成B事务后,再打开A事务,执行完成后提交事务
**//=======================//
必须在一个没有的事务中执行,否则抛出异常(与Propagation.mandatory相反)(never)
@Transactional(propagation = Propagation.NEVER)
public void methodA() {
methodB();
// do something
}
//=========分析==============/**
1、单独调用methodA时,因为当前有事务,会抛出异常,如果没有事务,则正常执行
**//=======================//
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务(supports)
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
methodB();
// do something
}
@Transactional(propagation = Propagation.NOT_SUPPORTS)
public void methodB() {
// do something
}
//=========分析==============//**
1、单独调用methodB时,以非事务执行。
2、调用methodA时,因为不存在事务,会开启一个事务A,
当执行到methodB方法时,会强制把A事务挂起,以非事务执行methodB,执行完成B在执行A事务
**//=======================
如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。
需要JDBC 驱动的java.sql.Savepoint类。使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true(属性值默认为false)。
PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:
它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。
使用 PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA事务管理器的支持。
使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED时,需要JDBC 3.0以上驱动及1.4以上的JDK版本支持。其它的JTATrasactionManager实现可能有不同的支持方式。
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。
另一方面, PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。
由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){
doSomeThingA();
methodB();
doSomeThingB();
}
@Transactional(propagation = Propagation.NEWSTED)
public void methodB(){
//……
}
//=========分析==============//**
1、如果单独调用methodB方法,则按REQUIRED属性执行
2、调用methodA时,上下文不存在事务,则创建事务A,当methodB方法调用之前,调用setSavepoint方法,保存当前的状态到savepoint。如果methodB方法调用失败,则恢复到之前保存的状态。但是需要注意的是,这时的事务并没有进行提交,如果后续的代码(doSomeThingB()方法)调用失败,则回滚包括methodB方法的所有操作。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
**//=======================
MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用,可能会造成
@Transactional(isolation = Isolation.READ_COMMITTED)
读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE)
串行化
添加这个类,根据知己需要修改切入点,然后放到能被spring boot扫描到的包下即可
/**
* @ClassName: GlobalTransactionAdviceConfig
* @Description: AOP全局事务管理配置
*
* 声明式事务说明:
* 1.如果将业务逻辑放到service层面来处理,则能够保证事务安全,即便使用了AOP来切入service方法也能保证事务安全;
* 2.如果多个service在controller层做业务逻辑(本身就是错误的),则不能保证事务安全。
* 对于2中的情况,应该尽量避免,因为本身就是错误的;
* 这种情况在面向切面编程中也有可能碰到,如,因为必要切入点为controller(应尽量避免,原则应切service),切面程序跟controller业务逻辑不同,
* service不同,会导致事务混乱;
*
* 如果出现上述情况,则可以使用编程式事务管理(也就是手动控制事务)
* 在controller逻辑开始之前手动开启/获取事务,然后在controller逻辑结束后再根据需要提交或者回滚事务;
* 在AOP中也是如此,在before中手动开启/获取事务(这一步是必须的),在after中处理切面逻辑,然后根据需要提交或者回滚事务,如果由于异常需要回滚事务,记得修改返回信息
*
**/
@Aspect
@Configuration
public class GlobalTransactionAdviceConfig {
private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.example.demo.service..*.*(..))";
@Autowired
private PlatformTransactionManager transactionManager;
@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("add*", txAttr_REQUIRED);
source.addTransactionalMethod("insert*", txAttr_REQUIRED);
source.addTransactionalMethod("save*", txAttr_REQUIRED);
source.addTransactionalMethod("create*", txAttr_REQUIRED);
source.addTransactionalMethod("delete*", txAttr_REQUIRED);
source.addTransactionalMethod("update*", txAttr_REQUIRED);
source.addTransactionalMethod("exec*", txAttr_REQUIRED);
source.addTransactionalMethod("set*", txAttr_REQUIRED);
source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("count*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("is*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("select*", txAttr_REQUIRED_READONLY);
return new TransactionInterceptor(transactionManager, source);
}
@Bean
public Advisor txAdviceAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
return new DefaultPointcutAdvisor(pointcut, txAdvice());
}
}
如果事务不在Service下面,而是在Controller中
切入点为controller时,如何使用编程式事务管理控制事务
@Component
@Aspect
public class ResUserAspect {
@Autowired
private IPartnerService partnerService;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
private TransactionStatus transactionStatus;
// 验证切入点为service时,AOP编程中的事务问题
@Pointcut("execution(public * com.example.demo.controllers.UserController.insertUser(..))")
private void insertUser(){}
@Before(value = "insertUser()")
public void before(){
//在切入点程序执行之前手动开启事务 - 必须的操作
transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
}
//切入点为controller时的事务验证
@Transactional
@AfterReturning(pointcut = "insertUser()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result){
if (!(result instanceof JSONMsg)){
System.out.println(result.getClass());
return;
}
JSONMsg jsonMsg = (JSONMsg) result;
try{
if (jsonMsg.getCode() == 200){
ResPartner partner = new ResPartner();
ResUser user = (ResUser) jsonMsg.getData();
partner.setId(user.getId());
partner.setName(user.getName());
partner.setDisplayName(user.getLogin());
int a = 1/0;
int i = partnerService.add(partner);
System.out.println(i);
}
platformTransactionManager.commit(transactionStatus); // 手动提交事务
System.out.println("提交事务");
}catch (Exception e){
platformTransactionManager.rollback(transactionStatus); // 出现异常,回滚事务
System.out.println("回滚事务");
System.out.println(e.getMessage());
//修改返回数据
jsonMsg.setCode(400);
jsonMsg.setMsg(e.getMessage());
}
}
}
@Autowired
private TransactionTemplate transactionTemplate;
public void testTranscation(){
// 设置隔离级别
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
// 设置传播属性
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
Boolean isSuccess = transactionTemplate.execute(new TransactionCallback<Boolean>() {
public Boolean doInTransaction(TransactionStatus status) {
Boolean result = true;
try {
// TODO
} catch (Exception e) {
status.setRollbackOnly();
result = false;
// TODO
}
return result;
}
});
}
//=========================测试==============//
@Component
public class TransactionalSample {
@Autowired
private ManualDemo manualDemo;
public void testManualCase() {
System.out.println("======= 编程式事务 start ========== ");
manualDemo.query("transaction before", 220);
manualDemo.testTransaction(220);
manualDemo.query("transaction end", 220);
System.out.println("======= 编程式事务 end ========== ");
}
}