springboot事务处理(自动/手动事务)

缇娜

今天写代码的时候遇到了这样的一个问题:通过this.saveData()调用saveData方法,这个方法被@Transactional注解,期望在 saveData 抛出错误时能够回滚数据库,结果事务并没有生效。然后便对 spring boot 的事务进行了一次更深入的学习。

学习笔记如下:

要使用事务首先需要开启事务,在启动类中加入注解:@EnableTransactionManagement

主要内容如下:

  • 自动事务及其原理
  • 手动事务的使用

自动事务

通过@Transactional开启事务。在UserService中定义一个事务方法save.然后在UserController中注入UserDao,并调用save方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Service
public class UserService{
@Autowired
private UserDao dao;

@Transactional(rollbackFor = Exception.class)
public void save(){
//一些数据库操作
}
}

@RestController
public class UserController{

@Autowired
private UserService service;

@GetMapping("/")
public String getInfo(){
service.save();
return "OK";
}
}

如上是一个典型的自动事务调用过程。但是如果是这样一种情况呢?现在UserService中增加一个方法saveAll,并在该方法中循环调用save方法,然后想让每一次的save方法都有事务支持,且相互不影响。saveAll代码如下:

1
2
3
4
5
public void saveAll(){
for(int i=0;i<10;i++){
this.save();
}
}

通常会想当然的认为 save 方法被@Transactional注解了,那么它就是支持事务的了,这种写法能够实现我们想要的效果。然而实际是不行的。

为什么 this 调用就不行呢?

原因就在Spring.spring 是一个 IOC 容器,它来统一管理所有的类,然后你想要用某个类的时候,它就会把这个类注入进入。关键就在这里:注入的这个类并不是这个类本身,然后这个类的代理。以代码来举例:

1
2
3
4
5
6
7
8
9
class UserController{

@Autowired
UserService userService;

public void doSomeThing(){
userService.save();
}
}

通过@Autowired注入的 userService 实际上并不是 UserService 的实例,userService 指向地址和这个类实例中this所指向的并不相同。实际调用save方法的并不是UserController类,而是Spring,这也就能解释为什么 Spring 能够帮我进行进行很多操作,比如自动事务,错误处理等。

如果我们通过 this 来调用 save 方法,显然是直接调用,不经过 spring 的代理,也就没有自动事务了。

那么如何才能实现上面想要的效果呢?

很简单,通过AopContext.currentProxy()来获取当前类的代理对象,这样调用save方法时就有自动事务了。使用该方法需要在启动类中加入如下注解:@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true),然后 pom 依赖中加入如下依赖:

1
2
3
4
5
<!--aop依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

或者使用手动事务

手动事务

手动事务就是通过代码来显式的进行事务操作。使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 需要先注入如下两个类
@Autowired
DataSourceTransactionManager dataSourceTransactionManager;
@Autowired
TransactionDefinition transactionDefinition;

public void saveAll(){
//开启事务
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
try{
this.save();
//提交事务
dataSourceTransactionManager.commit(transactionStatus);
}catch(Exception e){
//回滚事务
dataSourceTransactionManager.rollback(transactionStatus);
}
}

OK!

本文原创发布于:www.tapme.top/blog/detail/20190619

坚持原创技术分享,您的支持将鼓励我继续创作!