Spring Frameworkチュートリアル(6):トランザクション管理

1.コンセプト

  トランザクションは、データベース操作の最も基本的な単位であり、論理的な操作のセットです。すべてが成功します。1つの操作が失敗した場合、すべての操作が実行されるわけではありません。

  特徴:

  • Atomicity:この一連の操作は、一緒に実行するか、まったく実行しないでください。
  • 一貫性:データの一貫性を確保するために、たとえば、データが転送される場合、100元が受信者に追加されます。
  • 分離:トランザクションは相互に影響を与えることはできません。
  • 永続性:実行が成功すると、データベースへのトランザクションの変更は元に戻せません。

2.事業運営環境の構築

1.3層構造

  • Webレイヤー:ユーザー向け
  • サービス層:事業運営
  • Daoレイヤー:データベースへのアクセスと操作

  例として銀行振込を取り上げましょう。まず、daoレイヤーには、振込方式と振込方式の2つの方法が必要です。次に、サービス層で作成する必要のあるメソッドは、daoの2つのメソッドを呼び出すことによって実装される転送メソッドです。

2.特定の手順

  • 次のように、データベースspring_dbにテーブルpersonを作成します。

ここに画像の説明を挿入

  • 次のように、2つのレコードを手動で追加します。

ここに画像の説明を挿入

  • エンティティパッケージの下にPersonエンティティクラスを作成します。
	package com.wang.entity;
	
	public class Person {
    
    
	    private String person_id;
	    private String person_name;
	    private int person_money;
	
	    public Person(String person_id, String person_name, int person_money) {
    
    
	        this.person_id = person_id;
	        this.person_name = person_name;
	        this.person_money = person_money;
	    }
	
	    public String getPerson_id() {
    
    
	        return person_id;
	    }
	
	    public Person() {
    
    
	    }
	
	    public void setPerson_id(String person_id) {
    
    
	        this.person_id = person_id;
	    }
	
	    public String getPerson_name() {
    
    
	        return person_name;
	    }
	
	    public void setPerson_name(String person_name) {
    
    
	        this.person_name = person_name;
	    }
	
	    public int getPerson_money() {
    
    
	        return person_money;
	    }
	
	    public void setPerson_money(int person_money) {
    
    
	        this.person_money = person_money;
	    }
	}
  • Serviceパッケージとdaoパッケージの下にPersonServiceクラス、PersonDaoインターフェイス、およびPersonDaoImpl実装クラスを作成し、対応するアノテーションと特定のメソッドを追加します。
@Service
public class PersonService {
    
    
    @Autowired
    private PersonDao personDao;

    //转账操作
    public boolean transfer(Person p1,Person p2,int money){
    
    
        //先检查p1钱够不够
        if(personDao.queryMoneyById(p1)<money)return false;
        //钱够,转出,转入
        else{
    
    
            personDao.updateMoneyById(p1,money);
            personDao.updateMoneyById(p2,money*(-1));
        }
        return true;
    }
}

public interface PersonDao {
    
    

    void updateMoneyById(Person p2, int i);

    int queryMoneyById(Person p1);
}

@Component
public class PersonDaoImpl implements PersonDao {
    
    
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void updateMoneyById(Person p2, int i) {
    
    
        String sql="update person set person_money=person_money+? where person_id=?";
        Object[] arg={
    
    i,p2.getPerson_id()};
        jdbcTemplate.update(sql,arg);
        System.out.println("转账成功");
    }

    @Override
    public int queryMoneyById(Person p1) {
    
    
        String sql="select ifnull(person_money,0) from person where person_id=?";
        int money=jdbcTemplate.queryForObject(sql,Integer.class,p1.getPerson_id());
        System.out.println("得到id为"+p1.getPerson_id()+"的用户的余额为:"+money);
        return money;
    }
}
  • テストがあります:
	@Test
    public void testPerson(){
    
    
        ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
        PersonService personService=context.getBean("personService",PersonService.class);
        //转账
        Person p1=new Person();Person p2=new Person();
        p1.setPerson_id("1");p2.setPerson_id("2");
        int money=50;
        personService.transfer(p1,p2,money);
    }

ここに画像の説明を挿入

ここに画像の説明を挿入

3.トランザクションシナリオの紹介

  PersonServiceクラスの転送メソッドを見てみましょう。

 	//转账操作
    public boolean transfer(Person p1,Person p2,int money){
    
    
        //先检查p1钱够不够
        if(personDao.queryMoneyById(p1)<money)return false;
        //钱够,转出,转入
        else{
    
    
            personDao.updateMoneyById(p1,money);
            personDao.updateMoneyById(p2,money*(-1));
        }
        return true;
    }

  転出運転中にシステムに異常が発生した場合、p1の口座のお金が少なくても、p2の口座のお金が増えないという状況を想像してみてください。これが問題です。異常データの不整合につながります。

  この問題を解決するには、トランザクション操作(プログラミング実装)を導入する必要があり、その一般的な構造は次のとおりです。

try{
    
    
	//1开启事务

	//2业务操作

	//3若无异常,提交事务
}
catch(Exception e){
    
    
	//4出现异常,事务回滚
}

三、経営管理紹介

  • トランザクション管理をサービス層(ビジネスロジック層)に追加するのが最も適切です。
  • トランザクション管理を実装するには、プログラマティック(前のセクションで最後に示したものはプログラマティックであり、非常に不便です)と宣言型(推奨)の2つの方法があります。
  • 宣言型トランザクション管理には、注釈ベース(推奨)と構成ファイルの2種類があります。
  • 宣言型トランザクション管理、基本原則はAOPです。
  • 関連するインターフェースPlatformTransactionManagerには、フレームワークごとに異なる実装クラスがあります。

4つの宣言型トランザクション管理

1.アノテーションの実装

  • 構成ファイルでトランザクションマネージャーを構成します。
	<!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
  • tx名前空間の紹介:プロセスは省略されています

  • トランザクション注釈をオンにします。

	<!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
  • トランザクションアノテーション@Transactionalをサービスクラスまたはサービスクラスのメソッドに追加します。クラスに追加された場合、クラス内のすべてのメソッドがトランザクションになることを意味します。メソッドに追加された場合、このメソッドのみがトランザクションになることを意味します。

2.注釈のパラメーター構成

  @Transactionalアノテーションには、合計6つのパラメーターがあります。

  • 伝播:トランザクション伝播動作
  • 分離:トランザクション分離レベル
  • タイムアウト:タイムアウト期間
  • readOnly:読み取り専用かどうか
  • rollbackFor:ロールバック
  • noRollbackFor:ロールバックしないでください

(1)伝播:トランザクション伝播動作

  複数のトランザクションメソッド間での呼び出し、このプロセスでのトランザクションの管理方法。addメソッドとupdateメソッドの2つがあるとします。addメソッド呼び出しは内部的にupdateを呼び出します。

  • レベル1必須、add自体にトランザクションがあり、updateを呼び出した後、addのトランザクションも使用されます。add自体にトランザクションがない場合は、updateの呼び出し後にaddに新しいトランザクションが作成されます。
  • レベル2REQUIRED_NEWは、add自体にトランザクションがあるかどうかに関係なく、updateが呼び出された後にaddで新しいトランザクションが作成されます。

ここに画像の説明を挿入

  残りのいくつかのレベルは覚えておく必要はありませんが、最初の2つは覚えておく必要があります。

(2)分離:トランザクション分離レベル

  トランザクション間には分離があり、マルチトランザクション操作間の影響はありません。トランザクションの分離を考慮する必要があります。考慮しないと、ダーティ読み取り、繰り返し不可能な読み取り、仮想読み取りなどの問題が発生します。

  • ダーティリード:コミットされていないトランザクションは、コミットされていない別のトランザクションからデータを読み取ります。

ここに画像の説明を挿入

  • 繰り返し不可の読み取り:トランザクションのスコープ内の2つの同一のクエリは、異なるデータを返します。

ここに画像の説明を挿入

  • ファントム読み取り/ファントム読み取り:トランザクション内の同じ2回の読み取りで、2回目は別のトランザクションの新しく挿入された行を読み取ります。

  • トランザクション分離レベル:上記の問題の解決策です

ここに画像の説明を挿入

(3)タイムアウト:タイムアウト

  トランザクションは一定期間内にコミットする必要があり、コミットされていない場合はロールバックされます。デフォルト値は-1で、秒単位でタイムアウトがないことを意味します。

(4)readOnly:読み取り専用かどうか

  デフォルト値はfalseです。これは、読み取りだけでなく書き込みもできることを意味し、trueに設定できることを意味します。これは、クエリ操作のみを実行できることを意味します。

(5)rollbackFor:ロールバック

  トランザクションのロールバックで発生する例外を設定します。

(6)noRollbackFor:ロールバックなし

  トランザクションのロールバックなしで発生する例外を設定します。

3.xmlの実装

  • トランザクションマネージャーの構成:tx名前空間、接続プール、JdbcTemplate、トランザクションマネージャー
  • 通知の構成:機能拡張
	<!--配置通知-->
    <tx:advice id="txadvice">
        <!--配置事务参数-->
        <tx:attributes>
            <!--配置哪些方法,以及相应的事务属性-->
            <!--针对名为acountMoney-->
            <tx:method name="acountMoney" propagation="REQUIRED" isolation="DEFAULT"/>
            <!--针对所有以acount开头的方法名-->
            <tx:method name="account*"/>
        </tx:attributes>
    </tx:advice>
  • エントリポイントとアスペクトを構成する
	<!--配置切入点和切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="point" expression="execution(* com.wang.service.UserService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="point"/>
    </aop:config>

4.完全に注釈が付けられた開発(構成クラスの記述方法)

@Configuration
@ComponentScan(basePackages="com.wang")//组件扫描
@EnableTransactionManagement//开启事务
public class txConfig {
    
    
    //创建数据库的连接池
    @Bean
    public DruidDataSource getDruidDataSource(){
    
    
        DruidDataSource dataSource=new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///spring_db");
        dataSource.setUsername("root");
        dataSource.setPassword("wang1996526");
        return dataSource;
    }

    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
    
    
        JdbcTemplate jdbcTemplate=new JdbcTemplate();
        //注入dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
    
    
        DataSourceTransactionManager transactionManager=new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

おすすめ

転載: blog.csdn.net/Tracycoder/article/details/112916554