Spring 失效的场景

114 阅读3分钟

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();
}
}`

8.3.事务中避免远程调用