Spring 失效的的场景
1.访问权限的问题
访问权限必须是public才能保证事务不失效。
@Service public class UserService { @Transactional private void add(UserModel userModel) { saveData(userModel); updateData(userModel); } }
2 方法使用final 修饰
如果方法被final修饰,表示该类不允许被代理,我们知道事务底层使用的是代理模式,所以也就无法帮我们生成代理类,在代理中实现的事务功能。同样被static被修饰,也是无法通过动态代理,变成事务方法。
3.方法内部调用
有时我们需要在某个servie中某个方法中调用另外一个事务方法,例如:
`@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void add(UserModel userModel) {
userMapper.insertUser(userModel);
updateStatus(userModel);
}
@Transactional
public void updateStatus(UserModel userModel) {
doSameThing();
}
}`
3.1 解决办法1:新添加一个Service,把@Transaction加到新的service中。
`@Servcie
public class ServiceA {
@Autowired
prvate ServiceB serviceB;
public void save(User user) {
queryData1();
queryData2();
serviceB.doSave(user);
}
}
@Servcie
public class ServiceB {
@Transactional(rollbackFor=Exception.class)
public void doSave(User user) {
addData1();
updateData2();
}
}`
3.2 通过AopContent类
在该Service类中使用AopContext.currentProxy()获取代理对象
`@Servcie
public class ServiceA {
public void save(User user) {
queryData1();
queryData2();
((ServiceA)AopContext.currentProxy()).doSave(user);
}
@Transactional(rollbackFor=Exception.class)
public void doSave(User user) {
addData1();
updateData2();
}
}`
4.未被Spring管理
通常情况下,我们通过@Controller、@Service、@Component、@Repository等注解,可以自动实现bean实例化和依赖注入的功能。
5.多线程实现事务
如果在多线程中使用事务,
`@Slf4j
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional
public void add(UserModel userModel) throws Exception {
userMapper.insertUser(userModel);
new Thread(() -> {
roleService.doOtherThing();
}).start();
}
}
@Service
public class RoleService {
@Transactional
public void doOtherThing() {
System.out.println("保存role表数据");
}
}`
Spring 的事务是通过数据库来实现的,当前线程保存了一个map,key是数据源,value是数据通道。通过一个事务是指同一个数据库连接,只是拥有同一个数据库连接才会同时提交和回滚。
5.未开启事务
Springboot已经通过DataSourceTransactionManagerAutoConfiguration类,已经默认开启了事务。
6.自己在方法中捕捉了异常
@Slf4j @Service public class UserService { @Transactional public void add(UserModel userModel) { try { saveData(userModel); updateData(userModel); } catch (Exception e) { log.error(e.getMessage(), e); } } }
这种情况下spring事务当然不会回滚,因为开发者自己捕获了异常,又没有手动抛出,换句话说就是把异常吞掉了。 如果想要spring事务能够正常回滚,必须抛出它能够处理的异常。如果没有抛异常,则spring认为程序是正常的。
7. 编程式事务
在Spring中,提供编程式事务,TransactionTemplate,在他的excute方法中,就实现了事务的功能。 推荐使用编程式事务,由于SpringAop的问题,导致事务失效的问题。能够更小粒度的控制事务范围。 如果项目中有些业务逻辑比较简单,而且不经常变动,使用@Transactional注解开启事务开启事务也无妨,因为它更简单,开发效率更高,但是千万要小心事务失效的问题。
@Autowired private TransactionTemplate transactionTemplate; ... public void save(final User user) { queryData1(); queryData2(); transactionTemplate.execute((status) => { addData1(); updateData2(); return Boolean.TRUE; }) }
8.引起的大事务的问题解决
8.1少用@Transactional注解
可以使用编程式事务,在spring项目中使用TransactionTemplate类的对象,手动执行事务
8.2将查询(select)方法放到事务外
`@Autowired
private TransactionTemplate transactionTemplate;
public void save(final User user) {
queryData1();
queryData2();
transactionTemplate.execute((status) => {
addData1();
updateData2();
return Boolean.TRUE;
})
}`
如果使用@Transaction注解,
`@Servcie
publicclass ServiceA {
@Autowired
prvate ServiceA serviceA;
public void save(User user) {
queryData1();
queryData2();
serviceA.doSave(user);
}
@Transactional(rollbackFor=Exception.class)
public void doSave(User user) {
addData1();
updateData2();
}
}`