開啟事務過程中,如果遠程調用查詢當前已經開啟但沒有提交的事務,就會查不到數據。
示例代碼
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.Objects;@Slf4j
@RestController
@RequestMapping("simple")
@RequiredArgsConstructor
public class SimpleController {private final SimpleObjectMapper simpleObjectMapper;private final RestTemplate restTemplate;/*** 開啟事務過程中,如果遠程調用查詢當前已經開啟但沒有提交的事務,就會查不到數據。*/@GetMapping("insert")@Transactional(rollbackFor = Exception.class)//1.開啟事務public void insert() {SimpleObject simpleObject = new SimpleObject();simpleObject.setId(2);simpleObject.setName("name" + System.currentTimeMillis());simpleObjectMapper.insert(simpleObject);//2.因為開啟了事務,所以這里的 insert 并沒有 commit,導致下面遠程調用查詢數據是空的。SimpleObject simpleObject1 = restTemplate.getForEntity("http://localhost:8080/simple/2", SimpleObject.class, simpleObject.getId()).getBody();log.info("simpleObject1 (這里會輸出null):{}",simpleObject1);//3.這里會輸出null,因為事務沒有提交,數據庫不會新增數據if (Objects.isNull(simpleObject1)) {throw new RuntimeException("simpleObject1 is null");}}/*** 被遠程調用的查詢方法*/@GetMapping("{id}")public SimpleObject selectById(@PathVariable Integer id) {return simpleObjectMapper.selectById(id);}}
拋出異常
2024-05-23 22:49:44.033 INFO 8668 --- [nio-8080-exec-1] MyBatisSqlParsingPlugin : Execute SQL:
INSERT INTO simple_object ( id,name ) VALUES ( 2,'name1716475783993' )
2024-05-23 22:49:44.119 INFO 8668 --- [nio-8080-exec-2] MyBatisSqlParsingPlugin : Execute SQL:
SELECT id,name FROM simple_object WHERE id=2
2024-05-23 22:49:44.149 INFO 8668 --- [nio-8080-exec-1] SimpleController : simpleObject1 (這里會輸出null):null
2024-05-23 22:49:44.161 ERROR 8668 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] :
java.lang.RuntimeException: simpleObject1 is null
解決辦法
1、去掉@Transactional注解(顯然不可能,除非不影響正常的業務)
2、手動控制事務,但是有些情況下可能不適用,不適用的情況可以使用分布式事務,如:seata等(推薦)
手動控制事務
示例代碼
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.Objects;@Slf4j
@RestController
@RequestMapping("simple")
@RequiredArgsConstructor
public class SimpleController {private final SimpleObjectMapper simpleObjectMapper;private final RestTemplate restTemplate;private final PlatformTransactionManager platformTransactionManager;//事務管理器private final TransactionDefinition transactionDefinition;// 事務的一些基礎信息,如超時時間、隔離級別、傳播屬性等/*** 使用手動事務,遠程調用可以查詢到數據。*/@GetMapping("insert2")public void insert2() {//手動事務不能加@Transactional注解,否則優先使用@Transactional注解的事務TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);//1、手動獲取事務SimpleObject simpleObject = new SimpleObject();simpleObject.setId(2);simpleObject.setName("name" + System.currentTimeMillis());simpleObjectMapper.insert(simpleObject);platformTransactionManager.commit(transaction);//2.手動提交事務SimpleObject simpleObject1 = restTemplate.getForEntity("http://localhost:8080/simple/2", SimpleObject.class, simpleObject.getId()).getBody();log.info("simpleObject1 (這里會輸出simpleObject1):{}",simpleObject1);//3.這里會輸出id = 2的 SimpleObject 對象,因為事務提交,數據庫會新增數據if (Objects.isNull(simpleObject1)) {throw new RuntimeException("simpleObject1 is null");}}/*** 被遠程調用的查詢方法*/@GetMapping("{id}")public SimpleObject selectById(@PathVariable Integer id) {return simpleObjectMapper.selectById(id);}}
不拋出異常,可以正常獲取數據
2024-05-23 23:02:00.506 INFO 10608 --- [nio-8080-exec-1] c.f.m.plugin.MyBatisSqlParsingPlugin : Execute SQL:
INSERT INTO simple_object ( id,
name ) VALUES ( 2,
'name1716476520466' )
2024-05-23 23:02:00.592 INFO 10608 --- [nio-8080-exec-2] c.f.m.plugin.MyBatisSqlParsingPlugin : Execute SQL:
SELECT id,name FROM simple_object WHERE id=2
2024-05-23 23:02:00.641 INFO 10608 --- [nio-8080-exec-1] c.f.m.controller.SimpleController : simpleObject1 (這里會輸出simpleObject1):SimpleObject(id=2, name=name1716476520466)
不使用事務(不推薦)
示例代碼
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.Objects;@Slf4j
@RestController
@RequestMapping("simple")
@RequiredArgsConstructor
public class SimpleController {private final SimpleObjectMapper simpleObjectMapper;private final RestTemplate restTemplate;/*** 不使用事務,遠程調用可以查詢到數據。(不推薦)*/@GetMapping("insert3")public void insert3() {SimpleObject simpleObject = new SimpleObject();simpleObject.setId(2);simpleObject.setName("name" + System.currentTimeMillis());simpleObjectMapper.insert(simpleObject);//1、因為沒有事務,所以這里會新增數據到數據庫SimpleObject simpleObject1 = restTemplate.getForEntity("http://localhost:8080/simple/2", SimpleObject.class, simpleObject.getId()).getBody();log.info("simpleObject1 (這里會輸出simpleObject1):{}",simpleObject1);//2.這里會輸出id = 2的 SimpleObject 對象,因為事務提交,數據庫會新增數據if (Objects.isNull(simpleObject1)) {throw new RuntimeException("simpleObject1 is null");}}/*** 被遠程調用的查詢方法*/@GetMapping("{id}")public SimpleObject selectById(@PathVariable Integer id) {return simpleObjectMapper.selectById(id);}}
不拋出異常,可以正常獲取數據
2024-05-23 23:04:31.889 INFO 10608 --- [nio-8080-exec-6] c.f.m.plugin.MyBatisSqlParsingPlugin : Execute SQL:
INSERT INTO simple_object ( id,
name ) VALUES ( 2,
'name1716476671888' )
2024-05-23 23:04:31.895 INFO 10608 --- [nio-8080-exec-7] c.f.m.plugin.MyBatisSqlParsingPlugin : Execute SQL:
SELECT id,name FROM simple_object WHERE id=2
2024-05-23 23:04:31.897 INFO 10608 --- [nio-8080-exec-6] c.f.m.controller.SimpleController : simpleObject1 (這里會輸出simpleObject1):SimpleObject(id=2, name=name1716476671888)