SpringBoot2 | 第二十四篇(三):@Async与事务

​ 前面两篇,聊到了使用 @Async实现异步调用,@Async与异步线程池、异步异常处理;这篇笔记将记录使用 @Transactional方法中如何解决调用异步方法实现异步,@Async如何解决异步事务 问题。

[TOC]

问题场景

场景一

Spring 的异步执行注解 @Async,在 @Transactional 的方法中调用这个方法的时候发现,不对劲,耗时的逻辑我已经加入到异步去做了,怎么接口请求的响应这么慢,赶紧看日志,懵X,加了异步注解,却没有异步执行。

场景二

​ 在项目中用到 @Transactional 注解实现事务是必须滴,如果你还在用xml配置,那我只能说……。
但是有时候我们会发现在方法上加了@Transactional 注解却出现灵异事件,在方法内出现异常,数据还是插入到数据库,没有回滚,事务哪里去了,明明是加了的。

@Async与@Transactional

​ 这两个注解的共同点,都是通过 Spring AOP 的代理实现功能的。而 Spring AOP 的底层实现就是动态代理,CGLIB 和 JDK 。Spring AOP 会根据具体的实现不同,采用不同的代理方式。

@Transactional方法中如何解决调用异步方法实现异步

​ 当事务方法调用了同一个类中的异步方法时,异步注解@Async不起作用,@Async注解失效原因分析和解决方案

下面给出伪代码

1
2
3
4
5
6
7
8
9
10
11
12
class C {
@Transactional
function A {
print("A插入数据");
B();
}

@Async
function B {
print("B插入数据");
}
}

原因:程序运行后,由于方法 A 被 @Transactional 修饰,所以 A() 会被代理类调用(AOP 将事务嵌到方法 A 上),而 A() 中又直接调用了 方法 B ,这时候 方法 B 就变成了方法 A 中的一个普通方法,没有没代理类调用,实现不了 @Async 的功能,所以异步才会失效。

简单来说就是:@Transactional@Async 标记的方法 不允许被 ==同一个类中== 的其他方法 ==直接== 调用自己,同一类中直接调用则不会调用动态代理生成的ProxyClass(因为同一类中被调用方法的注解不被解析)

  • @Transactional 调用 @Async(失效)(本文就是例子)
  • @Transactional 调用 @Transactional(有效,其实是第一个 @Transactional 起的作用,参考事务默认的传播行为)
  • @Async 调用 @Transactional(失效)
  • @Async 调用 @Async(失效)

==三个小demo在 GitHub 上==

解决方法:

  1. 事务方法和异步方法不要在同一个类中(推荐

  2. 如果非要在同一个类中的话,那就使用代理类来调用异步方法即可

    • 在配置类(启动类也属于配置类)上加 @EnableAspectJAutoProxy(exposeProxy = true)(暴露代理类)

    • 在事务方法体中使用 AopContext.currentProxy()获得当前类的代理对象

      1
      2
      3
      // 获得当前代理对象
      当前类 service = (当前类) AopContext.currentProxy();
      log.info("当前对象的代理对象为:[{}]", service.getClass());

      伪代码如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      @EnableAspectJAutoProxy(exposeProxy = true)
      @Configuration // 启动类也属于配置类
      class D {
      }

      class C {
      @Transactional
      function A {
      print("A插入数据");
      C service = (C)AopContext.currentProxy();
      c.B();
      }

      @Async
      function B {
      print("B插入数据");
      }
      }

@Async如何解决异步事务

问题:事务方法调用异步方法时,怎么保证在异步方法中出现异常时,异步方法与事务方法中的事务都回滚

​ 如果在异步方法上加上 @Transactional 的话,是可以实现事务,但是由于是异步,线程不一样,所以他们两个不属于同一个事务, 不能保证异步方法出现异常,事务方法也跟着回滚。

伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@EnableAspectJAutoProxy(exposeProxy = true)
@Configuration // 启动类也属于配置类
class D {
}

class C {
// 事务方法
@Transactional
function A throws Exception {
print("A插入数据");
// 使用代理仅能让后面被调用的异步方法里的事务生效(但是与本方法的事务不属于同一个)
C service = (C)AopContext.currentProxy();
c.B();
}

// 异步方法
@Async
@Transactional
function B throws Exception {
print("B插入数据");
throw new RuntimeException(); // 抛异常
}
}

​ 在异步方法抛异常后,异步方法的事务会回滚,但是事务方法的事务就没有回滚了,这不是我们想要的结果。那么我们应该怎么实现异步方法抛异常,异步方法事务回滚,事务方法事务也要回滚呢?

解决方法:

​ 异步方法加 @Transactional 后,还需要结合 try...catch...finally... 使用。在方法体中 try 代码块 捕获异常,在 catch 代码块 中手动回滚事务,并把异常信息封装到 Future 对象 中,然后在 finally 代码块 中将 Future 对象 return 回主线程。这样,在主线程中通过判断 Future 对象 的对象类型从而决定是否抛出异常,如果 Future 对象 中封装的是异常对象,那么我们可以在主线程中直接将这个异常对象抛出(手动抛出异常的话事务则会自动回滚;我们也可以选择手动回滚,看需要吧),即可达到异步方法出现异常,事务方法收到 Future 对象中的异常信息,加几步处理后就能使双方都事务回滚。这就解决了 异步事务 了(两个方法不属于同一个事务,但通过 Future 对象实现异步方法一报错,双方都回滚的效果。)

伪代码如下:(想要解决异步事务,下面的姿势还算可以的)

注意: 由于异步方法要返回结果,所以在异步方法的catch中只能手动抛异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@EnableAspectJAutoProxy(exposeProxy = true)
@Configuration // 启动类也属于配置类
class D {
}

class C {
// 事务方法
@Transactional
function A throws Exception {
print("A插入数据");
C service = (C)AopContext.currentProxy();
Future<Object> future = c.B();
// 调用结果处理方法
deal(future);
}

// 异步方法
@Async
@Transactional
function Future<Object> B throws Exception {
String msg = "";
Future<Object> future = null;
try {
print("B插入数据");
msg = "判断是否正常插入,封装插入结果信息,成功或失败";
// throw new RuntimeException(); // 抛异常(去掉注释即异步方法抛异常)
future = new AsyncResult<>(Thread.currentThread().getName() + msg);
} catch(Exception e) {
future = new AsyncResult<>(new RuntimeException(e));
// 异步方法出现异常后 --> 手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
e.printStackTrace();
}
return future;
}

// 异步返回结果处理方法
fucntion deal(Future future) throws Exception {
Object o = future.get(5000, TimeUnit.SECONDS);
if (o instanceof Exception) {
// 异步方法返回异常结果
Exception e = (Exception) o;
// 手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
/**
* 抛出 RuntimeException 让它自动回滚(需要全局异常处理器统一
* 出来的话则抛个异常咯,不需要的话就手动回滚吧)
*/
// throw new RuntimeException(e);
} else if (o instanceof String){
log.info("[异步方法成功返回] --> {}", o);
}
}
}

环境/版本一览:

  • 开发工具:Intellij IDEA 2018.2.2
  • springboot: 2.0.6.RELEASE
  • jdk:1.8.0_171
  • maven:3.3.9
  • mysql-connector-java:8.0.13
  • druid-spring-boot-starter:1.1.9
  • lombok:1.18.2
  • spring-boot-starter-data-jpa:2.0.6.RELEASE

1、pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

2、application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
# druid 相关配置
druid:
driver-class-name: com.mysql.jdbc.Driver
# 基本属性
url: jdbc:mysql://localhost:3306/chapter24_3?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: 123456
jpa:
# 显示 sql
show-sql: true
# 数据库类型
database: mysql
# JPA 配置
hibernate:
# update:每次运行程序,没有表时会创建表,如果对象发生改变会更新表结构,原有数据不会清空,只会更新(推荐使用)
ddl-auto: update
# 指定生成的表的引擎为InnoDB类型(默认是MyISAM,MyISAM不支持事务)
database-platform: org.hibernate.dialect.MySQL57InnoDBDialect

3、entity

Order.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.fatal.entity;

import lombok.Data;
import lombok.experimental.Accessors;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;

/**
* Order 订单实体
* @author: Fatal
* @date: 2018/11/2 0002 9:47
*/
@Data
@Accessors(chain = true)
@Entity(name = "t_order")
public class Order implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

/** 金额 */
private Long amount;

/** 订单id */
private String orderId;

/** 联系方式 */
private String phone;

/** 用户id */
private String userId;

}

Message.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.fatal.entity;

import lombok.Data;
import lombok.experimental.Accessors;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;

/**
* Message 订单结果消息实体
* @author: Fatal
* @date: 2018/11/2 0002 11:13
*/
@Data
@Accessors(chain = true)
@Entity
public class Message implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

/** 内容 */
private String content;

/** 接收器 */
private String receiver;

}

4、dao

OrderRepository.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.fatal.dao.order;

import com.fatal.entity.Order;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

/**
* Order 数据库访问组件
* @author: Fatal
* @date: 2018/11/2 0002 10:13
*/
@Repository
public interface OrderRepository extends CrudRepository<Order, Long> {

}

MessageRepository.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.fatal.dao.message;

import com.fatal.entity.Message;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

/**
* Message 数据库访问组件
* @author: Fatal
* @date: 2018/11/2 0002 11:15
*/
@Repository
public interface MessageRepository extends CrudRepository<Message, Long> {

}

5、component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.fatal.component;

import com.fatal.dao.message.MessageRepository;
import com.fatal.entity.Message;
import com.fatal.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.concurrent.Future;

/**
* 异步组件
* @author: Fatal
* @date: 2018/11/3 0003 10:38
*/
@Slf4j
@Component
public class AsyncComponent {

@Autowired
private MessageRepository messageRepository;

/**
* 异步推送消息
*/
@Async
@Transactional
public Future<Object> sendMessage(Order order) {
Future<Object> future = null;
Long id = order.getId();
String messageStr = "";
String messageLog = "";
try {
if (id != null) {
messageStr = "下单成功(异步组件)";
// 推送逻辑...
messageLog = "[ --> 消息发送成功](异步组件)";
} else {
messageStr = "下单失败";
messageLog = "[ --> 消息发送失败](异步组件)";
}
// 保存数据
Message message = new Message().setContent(messageStr).setReceiver("接收器");
Message save = messageRepository.save(message);
// int i = 1/0; // 在异步组件中测试异步事务
if (save != null && save.getId() != null) {
log.info("保存订单结果成功(异步组件)[message = {}]", save);
} else {
log.info("保存订单结果失败(异步组件)");
}
log.info(Thread.currentThread() + messageLog);
future = new AsyncResult<>(Thread.currentThread().getName() + messageLog);
} catch (Exception e) {
future = new AsyncResult<>(new RuntimeException(e));
// 异步方法出现异常后 --> 手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
System.out.println("~~~~~~~~~~~~~~ " + Thread.currentThread().getName()+ " -- 异步方法抛异常啦 ~~~~~~~~~~~~~~");
e.printStackTrace();
}
return future;
}

}

6、service

IOrderService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.fatal.service;

import com.fatal.entity.Order;

/**
* Order 服务
* @author: Fatal
* @date: 2018/11/2 0002 10:31
*/
public interface IOrderService {

/**
* 事务方法内直接调用同一类中的异步方法(异步失效)
*/
Order saveAndAsyncNoWithProxy(Order order) throws Exception;

/**
* 事务方法内通过代理类调用同一类中的异步方法(异步生效)
*/
Order saveAndIdenticalClassAsyncWithProxy(Order order) throws Exception;

/**
* 事务方法内直接调用不同类中的异步方法(异步生效)
*/
Order saveAndDifferentClassAsyncWithProxy(Order order) throws Exception;

}

OrderServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package com.fatal.service.impl;

import com.fatal.component.AsyncComponent;
import com.fatal.dao.message.MessageRepository;
import com.fatal.dao.order.OrderRepository;
import com.fatal.entity.Message;
import com.fatal.entity.Order;
import com.fatal.service.order.IOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
* @author: Fatal
* @date: 2018/11/2 0002 10:34
*/
@Slf4j
@Service
public class OrderServiceImpl implements IOrderService {

@Autowired
private OrderRepository orderRepository;

@Autowired
private MessageRepository messageRepository;

@Autowired
private AsyncComponent asyncComponent;

/**
* 事务方法内直接调用同一类中的异步方法(异步失效)
*/
@Override
@Transactional
public Order saveAndAsyncNoWithProxy(Order order) throws Exception {
Order save = orderRepository.save(order);
// 推送消息
if (save != null) {
Future<Object> future = sendMessage(save);
Object o = future.get(5000, TimeUnit.SECONDS);
dealWithFuture(future);
}
log.info(Thread.currentThread() + "[ --> 下单结束:mark]");
return save;
}

/**
* 事务方法内通过代理类调用同一类中的异步方法(异步生效)
*/
@Override
@Transactional
public Order saveAndIdenticalClassAsyncWithProxy(Order order) throws Exception {
Order save = orderRepository.save(order);
// int i = 1/0; // 测试事务
// 推送消息
if (save != null) {
// 获得当前代理对象
OrderServiceImpl orderService = (OrderServiceImpl) AopContext.currentProxy();
log.info("OrderServiceImpl代理对象为:[{}]", orderService.getClass());
log.info("OrderServiceImpl对象为:[{}]", this.getClass());
// 调用异步方法获得调用结果,并对结果进行处理
Future<Object> future = orderService.sendMessage(save);
dealWithFuture(future);
}
log.info(Thread.currentThread() + "[ --> 下单结束:mark]");
return save;
}

/**
* 事务方法内直接调用不同类中的异步方法(异步生效)
*/
@Override
@Transactional
public Order saveAndDifferentClassAsyncWithProxy(Order order) throws Exception {
Order save = orderRepository.save(order);
// int i = 1/0; // 测试事务
// 推送消息
if (save != null) {
// 调用异步方法获得调用结果,并对结果进行处理
Future<Object> future = asyncComponent.sendMessage(save);
dealWithFuture(future);
}
log.info(Thread.currentThread() + "[ --> 下单结束:mark]");
return save;
}

/**
* 异步推送消息
*/
@Async
@Transactional
public Future<Object> sendMessage(Order order) {
Future<Object> future = null;
Long id = order.getId();
String messageStr = "";
String messageLog = "";
try {
if (id != null) {
messageStr = "下单成功";
// 推送逻辑...
messageLog = "[ --> 消息发送成功]";
} else {
messageStr = "下单失败";
messageLog = "[ --> 消息发送失败]";
}
// 保存数据
Message message = new Message().setContent(messageStr).setReceiver("接收器");
Message save = messageRepository.save(message);
// int i = 1/0; // 测试异步事务
if (save != null && save.getId() != null) {
log.info("保存订单结果成功[message = {}]", save);
} else {
log.info("保存订单结果失败");
}
log.info(Thread.currentThread() + messageLog);
future = new AsyncResult<>(Thread.currentThread().getName() + messageLog);
} catch (Exception e) {
future = new AsyncResult<>(new RuntimeException(e));
// 异步方法出现异常后 --> 手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
System.out.println("~~~~~~~~~~~~~~ " + Thread.currentThread().getName()+ " -- 异步方法抛异常啦 ~~~~~~~~~~~~~~");
e.printStackTrace();
} finally {
return future;
}
}

/**
* 异步结果出来
*/
private void dealWithFuture(Future future) throws Exception {
Object o = future.get(5000, TimeUnit.SECONDS);
if (o instanceof Exception) {
Exception e = (Exception) o;
System.out.println("~~~~~~~~~~~~~~ " + Thread.currentThread().getName()+ " -- 事务方法抛异常啦 ~~~~~~~~~~~~~~");
e.printStackTrace();
// 手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// 抛出 RuntimeException 让它自动回滚
// throw new RuntimeException(e);
} else if (o instanceof String){
log.info("[异步方法成功返回] --> {}", o);
}
}

}

7、Application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.fatal;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAspectJAutoProxy(exposeProxy = true) // 开启对AspectJ自动代理,并暴露代理组件
@EnableAsync //开启异步功能
@SpringBootApplication
public class Chapter243Application {

public static void main(String[] args) {
SpringApplication.run(Chapter243Application.class, args);
}
}

8、Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.fatal.test;

import com.fatal.entity.Order;
import com.fatal.service.order.IOrderService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
* 测试 @Transactional
* @author: Fatal
* @date: 2018/11/2 0002 10:30
*/
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class TransactionalTests {

@Autowired
private IOrderService orderService;

private Order order;

@Before
public void before() {
order = new Order().setOrderId("ORDER_123456")
.setUserId("USER_123456")
.setAmount(100000l)
.setPhone("137504123456");
}

/**
* 事务方法内直接调用同一类中的异步方法(异步失效)
* 注掉 int i = 1/0;
*/
@Test
public void saveAndAsyncNoWithProxy() throws Exception {
Order save = orderService.saveAndAsyncNoWithProxy(order);
log.info("新增订单成功[order = {}]", save);
}

/**
* 事务方法内通过代理类调用同一类中的异步方法(异步生效)
* 测试1:不打开 int i = 1/0; 测试 @Async是否起作用
* 测试2:打开 int i = 1/0; 测试 @Transactionl + @Async 异步事务
*/
@Test
public void saveAndIdenticalClassAsyncWithProxy() throws Exception {
Order save = orderService.saveAndIdenticalClassAsyncWithProxy(order);
log.info("新增订单成功[order = {}]", save);
}

/**
* 事务方法内直接调用不同类中的异步方法(异步生效)
* 测试1:不打开 int i = 1/0; 测试@Async是否起作用
* 测试2:打开 int i = 1/0; 测试 @Transactionl + @Async 异步事务
*/
@Test
public void saveAndDifferentClassAsyncWithProxy() throws Exception {
Order save = orderService.saveAndDifferentClassAsyncWithProxy(order);
log.info("新增订单成功[order = {}]", save);
}

}

显示

测试一

测试内容:事务方法内直接调用同一类中的异步方法(异步失效)。要求注掉 sendMessage() 中的 int i = 1/0;

运行 TransactionalTests.saveAndAsyncNoWithProxy();

显示

1541469082331

测试二

测试内容:事务方法内通过代理类调用同一类中的异步方法(异步生效)

运行 TransactionalTests.saveAndIdenticalClassAsyncWithProxy();

  1. 不打开 int i = 1/0; 测试 @Async是否起作用

    显示

    1541468943333

  2. 打开 int i = 1/0; 测试 @Transactionl + @Async 异步事务

    显示

    1541470294795

    打开数据库,可以发现数据都回滚了

测试三

测试内容:事务方法内直接调用不同类中的异步方法(异步生效)

运行 TransactionalTests.saveAndDifferentClassAsyncWithProxy();

  1. 不打开 int i = 1/0; 测试 @Async是否起作用

    显示

    1541469423120

  2. 打开 int i = 1/0; 测试 @Transactionl + @Async 异步事务

    显示

    1541470950731

    打开数据库,可以发现数据都回滚了

笔记

  1. 抛异常后还能return吗?

    不能,但是我们可以用 try…catch…finally… ,在捕获到异常后,我们在 catch 代码块 中对异常进行处理,然后在 finally 代码块中我们 return 即可

  2. @Transactional 标注的方法怎么手动回滚事务。

    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

  3. @Async 结合 @Transactional 使用,异步是有效果的。

  4. Future 的返回值为 void 的话,则可用于只有单边操作数据库的。

总结

SpringBoot的知识已经有前辈在我们之前探索了。比较喜欢的博主有:唐亚峰 | Battcn方志朋的专栏程序猿DD纯洁的微笑。对这门技术感兴趣的可以去他们的博客逛逛。谢谢他们的分享~~

以上文章是我用来学习的Demo,都是基于 SpringBoot2.x 版本。

源码地址: https://github.com/ynFatal/springboot2-learning/tree/master/chapter24_3