分別測試四種情況:
- 同一個service中,A方法調用B方法,B方法拋異常
- 同一個service中,A方法調用B方法,A方法拋異常
- service1中A方法,調用service2中B方法,B方法拋異常
- service1中A方法,調用service2中B方法,A方法拋異常
分別測試A、B方法加上@Transactional註解
Controller層:
@RestController
@RequestMapping(value = "/transaction")
public class TransactionController {
@Autowired
TransactionService transactionService;
@GetMapping(value="/test")
public String test() {
return transactionService.test();
}
}
Dao層:
@Mapper
public interface TransactionDao {
@Insert(value = "insert into test(name) values ('小明')")
public void insertUser();
@Insert(value = "insert into test(name) values ('小李')")
public void insertAnotherUser();
}
Service1:
@Service
public class TransactionService {
@Autowired
TransactionDao transactionDao;
@Autowired
TransactionServiceB transactionServiceB;
public String test() {
transactionDao.insertUser();
return "success";
}
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
}
Service2:
@Service
public class TransactionServiceB {
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
}
測試地址:http://localhost:8080/transaction/test
一、同一個service中,A方法調用B方法,B方法拋異常
1.1 方法A加@Transactional註解,方法B不加@Transactional註解
Service:
@Transactional
public String test() {
transactionDao.insertUser();
testException();
return "success";
}
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
結果:
數據庫:
事務成功回滾
1.2 方法A加@Transactional註解,方法B加@Transactional註解
Service:
@Transactional
public String test() {
transactionDao.insertUser();
testException();
return "success";
}
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
結果:
數據庫:
事務成功回滾
1.3 方法A不加@Transactional註解,方法B加@Transactional註解
Service:
public String test() {
transactionDao.insertUser();
testException();
return "success";
}
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
結果:
數據庫:
事務沒有回滾,B方法上的@Transactional註解失效。由於中間重置了一下自增的序列,因此id是從3開始,下面可以看到正常的主鍵的id的跳躍
二、 同一個service中,A方法調用B方法,A方法拋異常
2.1 方法A加@Transactional註解,方法B不加@Transactional註解
Service:
@Transactional
public String test() {
transactionDao.insertUser();
testException();
throw new RuntimeException();
}
public void testException() {
transactionDao.insertAnotherUser();
}
結果:
數據庫:
事務成功回滾
2.2 方法A加@Transactional註解,方法B加@Transactional註解
Serice:
@Transactional
public String test() {
transactionDao.insertUser();
testException();
throw new RuntimeException();
}
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
}
結果:
數據庫:
事務成功回滾
2.3 方法A不加@Transactional註解,方法B加@Transactional註解
Service:
public String test() {
transactionDao.insertUser();
testException();
throw new RuntimeException();
}
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
}
結果:
數據庫:
事務沒有回滾,B方法上的@Transactional註解失效,同時自增也間隔了4,說明之前的4次插入數據的回滾,會影響自增的遞增
三、 service1中A方法,調用service2中B方法,B方法拋異常
3.1 方法A加@Transactional註解,方法B不加@Transactional註解
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
return "success";
}
Service2:
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
結果:
數據庫:
事務回滾成功
3.2 方法A加@Transactional註解,方法B加@Transactional註解
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
return "success";
}
Service2:
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
結果:
數據庫:
事務回滾成功
3.3 方法A不加@Transactional註解,方法B加@Transactional註解
Service1:
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
return "success";
}
Service2:
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
結果:
數據庫:
Service1中沒有事務,也沒有回滾,Service2中的事務成功回滾
四、 service1中A方法,調用service2中B方法,A方法拋異常
4.1 方法A加@Transactional註解,方法B不加@Transactional註解
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
throw new RuntimeException();
}
Service2:
public void testException() {
transactionDao.insertAnotherUser();
}
結果:
數據庫:
事務回滾成功
4.2 方法A加@Transactional註解,方法B加@Transactional註解
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
throw new RuntimeException();
}
Service2:
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
}
結果:
數據庫:
事務回滾成功,同時可以說明,默認的傳播行爲REQUIRED,一會可以測試一下其他的傳播機制
4.3 方法A不加@Transactional註解,方法B加@Transactional註解
Service1:
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
throw new RuntimeException();
}
Service2:
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
}
結果:
數據庫:
Service1中沒有事務,也沒有進行回滾
五、測試其他傳播機制
5.1 採用4.2 方法A加@Transactional註解,方法B加@Transactional註解
進行測試,A方法拋出異常,修改方法B的傳播機制
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
throw new RuntimeException();
}
Service2:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testException() {
transactionDao.insertAnotherUser();
}
結果:
數據庫:
可以看到,Service1中的事務成功回滾,Service2中由於新開啓了一個事務,所以Service1中的回滾,並不會影響Service2中的數據的提交
5.2 採用3.2 方法A加@Transactional註解,方法B加@Transactional註解
進行測試,B方法拋出異常,修改方法B的傳播機制
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
return "success";
}
Service2:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
結果:
數據庫:
Service1和Sevice2中的事務都成功回滾
再進行一個嘗試,A方法中捕獲一下異常
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
try {
transactionServiceB.testException();
} catch (Exception e) {
return e.getMessage();
}
return "success";
}
Service2:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
結果:
返回空
數據庫:
Service1中捕獲了異常,因此Service1中的事務可以正常提交,Service2中由於出現異常,事務進行了回滾
六、 總結
同一個類中,A方法調用B方法,此時B方法的@Transactional註解是無效的,因爲沒有經過Spring的代理,直接對方法進行了調用,所以註解沒有辦法被AOP掃描到,就不能實現事務的功能。
不同類中,A方法調用B方法,此時B方法的@Transactional註解是生效的,因爲經過了Spring的代理。此時A方法或者B方法中出現異常,是否回滾需要看事務的傳播行爲,以及異常是否進行了捕獲。