爲什麼spring事務不生效?

開篇:總體戰線拉的有些長,所以沒有面面俱到,但有時間的話,後續文章會補上
環境介紹:win7,mysql5.6 ,mysql是新安裝的,如果沒有mysql的話,可以下載我上篇分享的鏈接,中間有段插曲,敘述如下,代碼集成完畢,但是事物是真的不生效,思來想去,想到mysql的儲存引擎是否是innodb,命令如下

show ENGINES; //顯示支持的儲存引擎,mysam不支持事物。
ALTER TABLE user ENGINE = InnoDB; 使用mysql innodb儲存引擎。
重試單元測試,可以正常事物回滾了。

原理簡述:spring事物是基於aop事物增強,aop底層實現倆種方式 一種是jdk,另一種是cglib
我們知道spring事物是基於動態代理實現,動態代理會有攔截器,對目標類進行處理,會在在目標方法開始執行之前創建並加入事務,並執行目標方法的邏輯, 最後根據執行情況是否出現異常,利用抽象事務管理器進行提交或者回滾,主要設計幾個類如下
TransactionManager
在這裏插入圖片描述
TransactionDefinition,定義了事物隔離級別等
在這裏插入圖片描述
在這裏插入圖片描述
TransactionAttribute繼承了TransactionDefinition
TransactionStatus
在這裏插入圖片描述

哪些方法能進行動態代理?不能代理意味着事物不生效
jdk:接口中的方法都是public ,所有隻能是 public 或 public final,同時不能使用static
cglib:不能使用 final,static 和 private 修飾符 ,因爲這些修飾符不能被子類覆蓋,cglib基於擴展子類來實現代理

爲什麼Transactional 只能應用到 public 方法纔有效 ?
源碼執行流程,
事物攔截器生產代理 --》invoke–》invokeWithinTransaction–》getTransactionAttribute–》computeTransactionAttribute
在這裏插入圖片描述

正確的設置@Transactional 的 rollbackFor 屬性,Transactional默認爲運行時異常,如果不指定異常,那麼非運行時異常不會回滾 測試方法3

如果傳播級別設置不對,也會發生事物不生效。
在這裏插入圖片描述

下面給出測試案例:

package com.example.demo.service.impl;

import com.example.demo.entity.Customer;
import com.example.demo.entity.User;
import com.example.demo.resp.UserRepository;
import com.example.demo.service.CustomerService;
import com.example.demo.service.UserService;
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;


@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {
    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;
    @Autowired
    CustomerService customerService;

    /**
     * 測試 事物是否正常,數據庫表中的數據爲空
     * 確實測試出問題了,再確定代碼無誤的情況下,請考慮數據庫的存儲引擎
     */
    @Test
    public void testSave() {
        User user = new User();
        user.setName("hhhh4");
        user.setEmail("[email protected]");
        userService.save(user);
    }

    /**
     * 事物正常回滾
     * save中爲運行時異常  int i=1/0
     */
    @Test
    public void testSave1() {
        User user = new User();
        user.setName("hhhh4");
        user.setEmail("[email protected]");
        userService.save(user);
    }

    /**
     * 同一個類中save方法沒有事物的方法,save2有事物的方法
     * 事物不會發生回滾,事物被忽略
     * <p>
     * 原因:只有目標方法由外部調用,目標方法才由 Spring 生成的代理對象來管理
     */
    @Test
    public void testSave5() {
        User user = new User();
        user.setName("hhhh5");
        user.setEmail("[email protected]");
        userService.save(user);
    }

    /**
     * 同一個類中倆個方法都有事物
     * 會發生回滾
     */
    @Test
    public void testSave6() {
        User user = new User();
        user.setName("hhhh6");
        user.setEmail("[email protected]");
        userService.save(user);
    }


    /**
     * 同一個類中 save 方法有事物,save2 沒有事物
     * 會發生回滾
     */
    @Test
    public void testSave7() {
        User user = new User();
        user.setName("hhhh7");
        user.setEmail("[email protected]");
        userService.save(user);
    }

    /**
     * 測試方法3
     */
    @Test
    public void testSave8() throws IllegalAccessException {
        User user = new User();
        user.setName("hhhh8");
        user.setEmail("[email protected]");
        userService.save3(user);
    }


    @Test
    public void testSave9() throws IllegalAccessException {
        User user = new User();
        user.setName("hhhh8");
        user.setEmail("[email protected]");
        userService.save3(user);
    }


    /**
     * 重點  應該由外部直接調用倆個方法,避免方法2 沒有被增強
     * 事物2 方法2中有事物註解,但是沒有被增強,所以事物並沒有被回滾
     */
    @Test
    public void testSave10() {
        customerService.save();
    }
}

參考文章:https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html
參考書籍:精通企業spring5.x
工程地址如下:https://github.com/ylha/spring-tx.git,看着很簡單吧? 自己試試 哈哈。總結的話都是測試中說了。

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