Spring中,@Transactional 註解的失效測試以及傳播行爲測試

分別測試四種情況:

  1. 同一個service中,A方法調用B方法,B方法拋異常
  2. 同一個service中,A方法調用B方法,A方法拋異常
  3. service1中A方法,調用service2中B方法,B方法拋異常
  4. 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方法中出現異常,是否回滾需要看事務的傳播行爲,以及異常是否進行了捕獲。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章