2019-06-19 20:30:42 +08:00
|
|
|
|
---
|
|
|
|
|
id: "20190619"
|
|
|
|
|
date: "2019/06/19 10:38:00"
|
|
|
|
|
title: "springboot事务处理(自动/手动事务)"
|
|
|
|
|
tags: ["java", "springboot", "transaction"]
|
|
|
|
|
categories:
|
|
|
|
|
- "java"
|
|
|
|
|
- "spring boot学习"
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
![缇娜](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190619175857.png)
|
|
|
|
|
|
|
|
|
|
今天写代码的时候遇到了这样的一个问题:通过`this.saveData()`调用`saveData`方法,这个方法被`@Transactional`注解,期望在 saveData 抛出错误时能够回滚数据库,结果事务并没有生效。然后便对 spring boot 的事务进行了一次更深入的学习。
|
|
|
|
|
|
|
|
|
|
学习笔记如下:
|
|
|
|
|
|
|
|
|
|
要使用事务首先需要开启事务,在启动类中加入注解:`@EnableTransactionManagement`
|
|
|
|
|
|
|
|
|
|
主要内容如下:
|
|
|
|
|
|
|
|
|
|
- 自动事务及其原理
|
|
|
|
|
- 手动事务的使用
|
|
|
|
|
|
|
|
|
|
# 自动事务
|
|
|
|
|
|
|
|
|
|
通过`@Transactional`开启事务。在`UserService`中定义一个事务方法`save`.然后在`UserController`中注入`UserDao`,并调用`save`方法。
|
|
|
|
|
|
2019-07-02 14:49:26 +08:00
|
|
|
|
<!-- more -->
|
|
|
|
|
|
2019-06-19 20:30:42 +08:00
|
|
|
|
```java
|
|
|
|
|
@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`代码如下:
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
public void saveAll(){
|
|
|
|
|
for(int i=0;i<10;i++){
|
|
|
|
|
this.save();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
通常会想当然的认为 save 方法被`@Transactional`注解了,那么它就是支持事务的了,这种写法能够实现我们想要的效果。然而实际是不行的。
|
|
|
|
|
|
|
|
|
|
## 为什么 this 调用就不行呢?
|
|
|
|
|
|
|
|
|
|
原因就在**Spring**.spring 是一个 IOC 容器,它来统一管理所有的类,然后你想要用某个类的时候,它就会把这个类注入进入。关键就在这里:注入的这个类并**不是这个类本身**,然后这个类的**代理**。以代码来举例:
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
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`方法时就有自动事务了。
|
|
|
|
|
|
|
|
|
|
或者使用手动事务
|
|
|
|
|
|
|
|
|
|
# 手动事务
|
|
|
|
|
|
|
|
|
|
手动事务就是通过代码来显式的进行事务操作。使用方法如下:
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
// 需要先注入如下两个类
|
|
|
|
|
@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](https://www.tapme.top/blog/detail/20190619)
|