面試的時候遇到過這個問題,當時一臉懵逼。現在記錄一下。。。
@Transactional失效場景
1. 在類內部呼叫呼叫類內部@Transactional標註的方法
1.1 定義一個錯誤的@Transactional標註實現,設定一個內部呼叫
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
UserMapper userMapper;
@Override
public void oneTest() {
oneTestNoPublic();
}
@Transactional(rollbackFor = Exception.class)
public void oneTestNoPublic(){
// 插入一條資料
int num = userMapper.insert(new User("小紅", "青島市", 18));
if (num > 0){
// 插入後製造一個錯誤
System.out.println(1 / 0);
}
// 再次插入一條資料
userMapper.insert(new User("小強", "煙臺市", 21));
}
}
1.2 測試用例
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {
@Resource
IUserService userService;
/**
* 在類內部呼叫呼叫類內部@Transactional標註的方法
*/
@GetMapping("/one")
public void oneTest(){
try {
userService.oneTest();
} catch (Exception e){
e.printStackTrace();
}
}
}
1.3 執行後,控制檯報錯:
1.4 檢視資料庫,發現並沒有回滾new User("小紅", "青島市", 18)這條資料
呼叫一個方法在類內部呼叫內部被@Transactional標註的事務方法,執行結果是事務不會正常開啟。userMapper.insert(new User("小紅", "青島市", 18)) 操作沒有進行回滾。
2. @Transactional註解標註方法修飾符為非public
2.1 新寫一個TestServiceImpl,將@Transactional註解標註方法修飾符為非public
@Service
public class TestServiceImpl {
@Resource
UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
void twoTestNoPublic(){
// 插入一條資料
int num = userMapper.insert(new User("小紅", "青島市", 18));
if (num > 0){
// 插入後製造一個錯誤
System.out.println(1 / 0);
}
// 再次插入一條資料
userMapper.insert(new User("小強", "煙臺市", 21));
}
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
TwoTestServiceImpl twoTestService;
@Override
public void twoTest() {
twoTestService.twoTestNoPublic();
}
}
2.2 測試用例
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {
@Resource
IUserService userService;
/**
* @Transactional註解標註方法修飾符為非public
*/
@GetMapping("/two")
public void twoTest(){
try {
userService.twoTest();
} catch (Exception e){
e.printStackTrace();
}
}
}
2.3 執行後,控制檯報錯:
2.4 檢視資料庫,發現並沒有回滾new User("小紅", "青島市", 18)這條資料
以上的訪問方式,導致事務沒開啟,因此在方法丟擲異常時,userMapper.insert(new User("小紅", "青島市", 18)) 操作沒有進行回滾。如果oneTestNoPublic()方法改為public的話將會正常開啟事務,userMapper.insert(new User("小紅", "青島市", 18)) 將會進行回滾。
3. 事務方法內部捕捉了異常,且沒有丟擲新的異常
3.1 寫一個內部捕捉異常,且不再丟擲新的異常的方法
@Service
public class TestServiceImpl {
@Resource
UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
public void threeTestNoPublic(){
// 插入一條資料
int num = userMapper.insert(new User("小紅", "青島市", 18));
if (num > 0){
try {
// 插入後製造一個錯誤
System.out.println(1 / 0);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
// 再次插入一條資料
userMapper.insert(new User("小強", "煙臺市", 21));
}
}
3.2 測試用例
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {
@Resource
IUserService userService;
/**
* 事務方法內部捕捉了異常,且沒有丟擲新的異常
*/
@GetMapping("/three")
public void threeTest(){
try {
userService.threeTest();
} catch (Exception e){
e.printStackTrace();
}
}
}
3.3 執行後,控制檯報錯:
3.4 檢視資料庫,發現並沒有回滾new User("小紅", "青島市", 18)這條資料
如果需要在catch裡面回滾資料的話,就需要丟擲新的異常或者在catch裡面使用如下程式碼:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
好了,上面三種就是@Transactional註解不起作用,@Transactional註解失效的主要原因。