Spirng のトランザクション メソッド A は、トランザクションが無効かどうかにかかわらず、メソッド B を呼び出します。

Springboot はトランザクション メソッドを開き、トランザクションなしでメソッドを呼び出します。
提示:上方标题是一个很笼统的场景,详情展开如下,先说结论:

概要:
メソッド A がメソッド B を呼び出す:
シナリオ 1 メソッド A と B が同じクラスにある場合:
A に @Transactional の注釈が付けられ、B に @Transactional の有無にかかわらず注釈が付けられ、トランザクションが有効な場合、AB は同じクラスにあります。取引。
A が @Transactional アノテーションを追加せず、B が @Transactional アノテーションを追加した場合、トランザクションは無効になります。( 但是如果使用代理类来进行b方法调用,那么B会开启事务,A不会开启事务)

シナリオ 2 A と B が同じクラスにない場合:
A に @Transactional アノテーションが付けられ、B に @Transactional アノテーションが付いているかどうかに関係なく、トランザクションは有効です。
A が @Transactional アノテーションを追加せず、B が @Transactional アノテーションを追加した
場合、B のみがトランザクションを持ちます。A が @Transactional アノテーションを追加せず、B が @Transactional アノテーションを追加しなかった場合、A と B の両方にはトランザクションがありません。

または、次のように理解します。
1. A が @Transactional アノテーションを追加した場合、それがクラス内であるかどうか、B がアノテーションを追加するかどうかに関係なく、AB は同じトランザクション内にあります。 2. A が @Transactional アノテーションを追加しない場合、B のみが追加されます
。 @Transactional を追加します。注: AB メソッドは同じクラスに属しており、トランザクションは無効です (プロキシ クラスを使用すると、B メソッドにはトランザクションがあります)。AB は別のクラスに属し、B のみにトランザクションがあります。3. A が存在しない場合
@Transactional アノテーションを追加し、B が @Transactional アノテーションを追加しない場合、トランザクションは存在しません。

理由: A メソッドに @Transactional アノテーションが付いているため、Spring が管理するとプロキシ クラスが生成されます。実際に A メソッドが呼び出される際には、実際にプロキシ クラス内のメソッドが実行されます。プロキシ クラス内のメソッドには、既にB メソッド。呼び出しはメソッドになりました。したがって、トランザクションは有効です。

注:コード内の TestUserServiceImpl のupdate方法合計はupdateOther方法、全文では A メソッドと呼ばれます。

シナリオ 1 A メソッドと B メソッドは同じクラスにあります。

まずコントローラー、サービス、マッパーを自分で作成します。コードは以下のように表示されます

@RestController
@RequestMapping("/testUser")
public class TestShiWuController {
    
    

    @Autowired
    private ITestService testService;
    
    @GetMapping("/update")
    public void update(String param) {
    
    
        testService.update(param);
    }
}
@Service
public class TestUserServiceImpl implements ITestService {
    
    

    @Autowired
    private TestUserMapper testUserMapper;

    @Override
    @Transactional(rollbackFor =Exception.class)
    public int update( String param) {
    
    

        testUserMapper.updateUserById("a",1L);
        b(param);
        c(param);
        return 0;
    }

//    @Transactional(rollbackFor =Exception.class)
    public void b(String s) {
    
    
        TestUser testUser = testUserMapper.selectById(1L);
        System.out.println(testUser);
        testUserMapper.updateUserById("b",2L);
        if (s.equals("b")) {
    
    
            int a = 1/0;
        }
    }

//    @Transactional(rollbackFor =Exception.class)
    public void c(String s) {
    
    

        TestUser testUser = testUserMapper.selectById(2L);
        System.out.println(testUser);
        testUserMapper.updateUserById("c",3L);
        if (s.equals("c")) {
    
    
            int a = 1/0;
        }
    }
}
@Mapper
public interface TestUserMapper extends BaseMapper<TestUser> {
    
    
    @Update("update testUser set name = #{name} where id=#{id}")
    public int updateUserById(String name, Long id );
}

1.1 如果A加@Transactional注解,B不加@Transactional注解:

param=b の場合、リクエスト後の結果は次のようになります。
ここに画像の説明を挿入

注意: Q: アイデア コンソールによると、メソッド b は id=1 のレコードをクエリしていることがわかりますが、なぜ name=1 ではなく name=a なのでしょうか?
回答: この時点でクエリ レコードの name=a となっているのは、b が a のトランザクション内にあり、同一事务内数据是可见共享的この時点では name=a がデータベースに登録されていないためです。为了验证此处メソッドの最後に Thread.Sleep(10000) を追加して 10 秒間スリープし、インターフェイスを呼び出してこの 10 秒間にパラメータ param=a を渡します。navicat に移動して、id=1 のレコードをクエリします。データベースで、それが 1 であるかどうかを確認してください。1 であれば、上記が正しいことが証明されます。

原因分析:

ヒント: メソッド b と c がメソッド a のメイン トランザクションに追加されていることがわかります。つまり、3 つのメソッドは同じトランザクション内にあります。

数据都变成了初始值分析: param=b を指定してインターフェイスを呼び出した後、直感的に、つまりロールバックを確認できます。(実際には、@Transional アノテーションで b メソッドが追加されているかどうかに関係なく、トランザクションは有効になります。つまり、両方のメソッドが同じトランザクション内にあり、ブロガーが個人的にテストしました)

1.2 如果update方法不加@Transactional注解,B加@Transactional注解:

コードを次のように変更します。
ここに画像の説明を挿入

param=b の場合、リクエスト後の結果は次のようになります。
ここに画像の説明を挿入

注意此时name=a和上方1.1标题的name=a有什么区别吗質問: アイデア コンソールによると、メソッド b が id=1 、?のレコードをクエリしたことがわかります。
回答: 今回クエリされたレコード名 = a は、更新メソッドの変更がすでにデータベースに反映されているためであり、トランザクションとは関係ありません。为了验证此处メソッドの最後に Thread.Sleep(10000) を追加して 10 秒間スリープし、インターフェイスを呼び出してこの 10 秒間にパラメータ param=a を渡します。navicat に移動して、id=1 のレコードをクエリします。データベースを調べて、名前が a であるかどうかを確認し、10 秒以内に Name=a であれば、上記が正しいことが証明されます。

1.3 如果update方法不加@Transactional注解,B加@Transactional注解,并且使用代理类调用B方法:

コードを次のように変更します。
ここに画像の説明を挿入



ここに画像の説明を挿入
図に示すように、param=b の場合、リクエスト後の結果は次のようになります。

結論: このクラスでは、トランザクションをオープンしないメソッド A が次のようになります。

1. トランザクションを開始したメソッド B を直接呼び出します。AB はトランザクションを開始しません。
2. ただし、A メソッドがプロキシ オブジェクトを使用して B メソッドを呼び出す場合、B メソッドはトランザクションを開きますが、A メソッドにはトランザクションがありません。

シナリオ 1 の要約分析:

分析:
シナリオ 1.2 param=b の場合、id=1 と id=2 のレコードが観察されます并未回滚update方法没有开启事务,b方法的事务也失效了。
シナリオ 1.3 では、param=b の場合、id=1 のレコードが観察できることがわかります并未回滚id=2 のレコードにはデータがロールバックされています。このことから、A方法没有开启事务,B方法的开启了事务。
上記の状況がなぜ起こるのかということが考えられます。
回答: このクラスでは、メソッド A がメソッド B を直接呼び出すことは、this.b() を使用するのと同等であるため、詳細については、以下のテクスチャ クラスのコンパイル ファイルを参照してください。@Transitional に含まれるトランザクション原則は AOP に基づいているため、トランザクションを有効にするには、プロキシ オブジェクトを通じてメソッドを呼び出す必要があります。この呼び出しは、ネイティブ オブジェクト呼び出しと同等です (トランザクション プロキシによって拡張されていません)。

備考: 私の開発環境ではspringboot 2.3版本,AOP默认代理是Cglib、プロキシ オブジェクトは、implements インターフェイスを使用せずに、このクラスのメソッドを直接呼び出すことができ、ServiceImpl の強制型変換も直接実行できます。(jdkエージェントの場合はエラーが報告されます) もちろん自己注入自己Bメソッドを呼び出すこともできますが、必ずImplementsメソッドを使用する必要があります。


シナリオ 2 A メソッドと B メソッドは同じクラスにありません。

注意:
TestUserServiceImpl的updateOther 就是A方法
GoodsServiceImpl的updateGoodsById 就是B方法
まず、比較方法に対応するトランザクションの具体的な状況 (トランザクション伝播レベル) を観察するために、別の操作クラスを独自に作成します。コードは以下のように表示されます

@Service
public class TestUserServiceImpl implements ITestService {
    
    

    @Autowired
    private TestUserMapper testUserMapper;
    @Autowired
    private IGoodsService goodsService;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateOther(String param) throws InterruptedException {
    
    
        // 该testUser表记录,修改前 name = '1'
        testUserMapper.updateUserById("a", 1L);
        // 该goods表记录,修改前 name = '1'
        goodsService.updateGoodsById("product1", 1L);
        return 1;
    }
}
@Service
public class GoodsServiceImpl implements IGoodsService {
    
    

    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private TestUserMapper testUserMapper;

//    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateGoodsById(String name, Long id) {
    
    
        TestUser testUser = testUserMapper.selectById(1L);
        System.out.println("GoodsServiceImpl.updateGoodsById()查询的user结果: " + testUser.toString());
        goodsMapper.updateGoodsById(name, id); //          修改前 name = '1'
        int i = 1 / 0;
        return 0;
    }
}
@Mapper
public interface GoodsMapper extends BaseMapper<Goods> {
    
    
    @Update("update goods set name = #{name} where id=#{id}")
    public int updateGoodsById(String name, Long id );
}

2.1 如果A方法加@Transactional注解,b方法不加@Transactional注解:

リクエスト後の結果は次のようになります。
ここに画像の説明を挿入

2.1 現象の分析: param=b の場合、mysql 内のデータは変更されておらず、両方がトランザクション データをロールバックしたことを示し、両方がトランザクションを開始したことを示します。そして、メソッド a のデータ変更はメソッド b でクエリされ、2 つが同じトランザクションであることが示されます。

結論: 異なる種類のメソッドが呼び出される場合 (A が B を呼び出す)、メソッド A に @Transional が追加され、メソッド B に @Transional が追加されない場合、メソッド B はメソッド A のトランザクションに追加されます。

(B メソッドが @Transional を追加すると、Spring のトランザクションのデフォルトの伝播レベルにより、AB メソッドも同じトランザクション内になります)

2.2 如果A方法不加@Transactional注解,B加@Transactional注解:

リクエスト後の結果は次のようになります。
ここに画像の説明を挿入

注意此时name=a和上方2.1标题的name=a有什么区别吗質問: アイデア コンソールによると、メソッド b が id=1 、?のレコードをクエリしたことがわかります。
回答:
2.2 シナリオ b のメソッド クエリの name=a は、メソッド A でトランザクションがオープンされておらず、データ更新がデータベースに直接格納されているためです。このとき、クエリされた name=a は、データベース。
2.1 シナリオ b のメソッド クエリの name=a は、2 つが同じトランザクション内にあり、データが共有されており、ある種のスナップショット読み取りのようです (詳細は忘れましたが、mvcc とは何ですか)。

2.2 現象の分析: メソッド b の例外のため、mysql の商品テーブルのみにデータがロールバックされており、メソッド B のみがトランザクションを開始したことを示しています。

結論: 異なるクラスでは、A は @Transitional メソッドを追加するために B を呼び出す @Transitional メソッドを追加せず、A はトランザクションを開始せず、B はトランザクションを開始します。


———————————————
著作権表示: この記事の冒頭は以下のリンクを元にしています
元のリンク: https://blog.csdn.net/u012279452/article/details/126505417

おすすめ

転載: blog.csdn.net/lzq2357639195/article/details/129633850