参考:https://blog.csdn.net/huangjun0210/article/details/84202333 这位大哥写得很好,后来我才看到
首先上SpringBoot事务注意事项
6.事务被捕捉了则无法回滚事务;
第3点:修改捕捉指定异常:
@Transactional(rollbackFor=Exception.class)
第5点:说白了就是同一个C类里面(含有方法A,方法B),如果方法A调用事务方法B。则事务会失效;
要改成方法A放到另一个类(D)当中,方法B放在不同的类当中才会生效;
注解@transactional的底层实现是Spring AOP技术,而Spring AOP技术使用的是动态代理。这就意味着对于静态(static)方法和非public方法,注解@Transactional是失效的。
自调用是指一个类的一个方法去调用自身另外一个方法的过程。在自调用的过程中,是类自身的调用,而不是代理对象去调用, 那么就不会产生 AOP,这样 Spring就不能把你的代码织入到约定的流程中。
为了克服这个问题,一方面可以写两个Service,用一个Service去调用另一个Service,这样就是代理对象的调用。Spring才会将你的代码织入事务流程。另一方面,也可以从Spring IoC容器中获取代理对象来启用AOP。从Spring IoC容器中获取代理对象的代码实例如下:
DEFAULT
:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是: READ_COMMITTED
。 READ_UNCOMMITTED
:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读;几乎不用READ_COMMITTED
:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。 (
博主通常可能用多个地方触发某字段更新操作,以最后一个事务提交为准:
例子.用户A修改用户名先开启事务,后台管理员后修改用户A的用户名,则以后台管理员修改为准;
解释:因为在某些情况下,用户A需要执行的事务很长(时间),在这个时候管理员再去修改(比用户A先执行完提交更新事务),在用户A事务提交后,则用户A是最后修改,相当于管理员修改失败了;
这个时候如果开启了该级别,那么后台管理员所触发事务会等待用户A提交完毕,可能出现延迟、锁报错;不可避免幻读
总结;一般博主只使用在后台管理系统,因为会出现延迟、等待的问题导致报错,要是Api前台使用会比较慢;
)REPEATABLE_READ
:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
(
博主通常可能用在区间统计业务(还没用过,要是有这种业务一般我在java中处理了)
例子.下午五点统计钱包流水表今天的所有数据;需要查询两次同样的数据;
解释:查出来在java中分别计算(在这个期间你可能代码写错了,导致数据错了),这个时候你需要再查一遍数据库进行统计对比
这个时候你开启了这个级别;你统计的数据与数据库不一样,那么你就可以监测到自己统计错误了;
总结;很少用到
)
SERIALIZABLE
:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。(几乎不用)
package org.springframework.transaction.annotation;
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
private final int value;
private Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
2.1 REQUIRED(0)
需要事务,它是默认传播行为,如果当前存在事务,就沿用当前事务,否则新建一个事务运行子方法。
2.2 SUPPORTS(1)
支持事务,如果当前存在事务,就沿用当前事务,如果不存在,则继续采用无事务的方式运行子方法。
2.3 MANDATORY(2)
必须使用事务,如果当前没有事务,则会抛出异常,如果存在当前事务,就沿用当前事务。
2.4 REQUIRES_NEW(3)
无论当前事务是否存在,都会创建新事务运行方法,这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立。
2.5 NOT_SUPPORTED(4)
不支持事务,当前存在事务时,将挂起事务,运行方法。
2.6 NEVER(5)
不支持事务,如果当前方法存在事务,则抛出异常,否则继续使用无事务机制运行。
2.7 NESTED(6)
在当前方法调用子方法时,如果子方法发生异常,只回滚子方法执行过的SQL,而不回滚当前方法的事务。
常用的传播行为主要有三种:REQUIRED 、REQUIRES_NEW、 NESTED。
3. @Transactional的自调用失效问题
注解@transactional的底层实现是Spring AOP技术,而Spring AOP技术使用的是动态代理。这就意味着对于静态(static)方法和非public方法,注解@Transactional是失效的。
自调用是指一个类的一个方法去调用自身另外一个方法的过程。在自调用的过程中,是类自身的调用,而不是代理对象去调用, 那么就不会产生 AOP,这样 Spring就不能把你的代码织入到约定的流程中。
为了克服这个问题,一方面可以写两个Service,用一个Service去调用另一个Service,这样就是代理对象的调用。Spring才会将你的代码织入事务流程。另一方面,也可以从Spring IoC容器中获取代理对象来启用AOP。从Spring IoC容器中获取代理对象的代码实例如下:
import com.springboot.web.dao.UserDao;
import com.springboot.web.model.User;
import com.springboot.web.service.UserService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class UserServiceImpl implements UserService, ApplicationContextAware {
@Autowired
UserDao userDao;
private ApplicationContext applicationContext;
//实现生命周期方法,设置Ioc容器
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
public void createUsers(List
UserService userService = applicationContext.getBean(UserService.class);
for(User user : userList){
userService.createUser(user);
}
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRES_NEW)
public void createUser(User user){
userDao.createUser(user);
}
}
}