Spring学習ノート③:JDBCと事務管理


文書ディレクトリ
  • 0.基本概念
  • 1.JDBTtemplate使用例
  • 1.1データソースおよび構成
  • 1.2データベース構成
  • 1.3クエリ文の例
  • 2.Springトランザクション制御
  • 2.1プロファイル
  • 2.2トランザクションインタフェース
  • 2.2.1取引管理:PlatformTransactionManager
  • 2.2.2取引情報:TransactionDefinition
  • 2.2.3取引ステータス:TransactionStatus
  • 2.3 Springの宣言トランザクション管理
  • 2.3.1例①: XMLベースの宣言トランザクション管理
  • 2.3.2 tx:methodのプロパティ詳細
  • 2.3.3例②:注釈ベースの宣言トランザクション管理
  • 付録学習ノート①~③コード及び工程書類
  • この3つのノートをコレクションし、Springのよくある問題と使用方法を完全にレビューします.
  • Spring学習ノート①: IoC容器、Beanと注入
  • Spring学習ノート②:ダイナミックエージェントおよびフェース向けプログラミング
  • Spring学習ノート③: JDBCと事務管理(本編)
  • 0.基本概念
  • Springフレームワークで提供されるJDBCサポートは、core(コアパッケージ)、object(オブジェクトパッケージ)、dataSource(データソースパッケージ)、support(サポートパッケージ)の4つのパッケージで構成されています.
  • 
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-jdbcartifactId>
      <version>5.2.6.RELEASEversion>
    dependency>
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-txartifactId>
        <version>5.2.6.RELEASEversion>
    dependency>
    
    <dependency>
      <groupId>mysqlgroupId>
      <artifactId>mysql-connector-javaartifactId>
      <version>8.0.20version>
    dependency>
    

    1.JDBCTEmplate使用例
    1.1データソースと構成
    データ・ソースの基本クラスはorg.springframework.jdbc.dataSource.DriverManagerDataSourceで、主な機能はデータベース接続を取得することであり、バッファ・プール、分散トランザクションなどを導入することもでき、以下の構成が必要です.
     
    <bean id="dataSource" class="org.springframework.jdbc.dataSource.DriverManagerDataSource">
      
      
      <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" /> 
      
      
      
      <property name="username" value="$root" />
      
      <property name="password" value="$password" />
      
      <property name="initialPoolSize" value="3">property>
      <property name="maxPoolSize" value="10">property>
      <property name="maxStatements" value="100">property>
      <property name="acquireIncrement" value="2">property>
    
    bean>
    

    コアクラスはorg.springframework.jdbc.core.JDBCTemplateで、データ・ソースをロードしてインスタンス化する必要があります.
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.jdbcTemplate">
      
      <property name="dataSource" ref="dataSource"/>
    bean>
    

    プロファイルクラスを使用して、上記の構成を完了することもできます.
    @Bean
    public DriverManagerDataSource dataSource(){
        String url = "jdbc:mysql://localhost:3306/spring?serverTimezone=Asia/Shanghai";
        String username = "root";
        String password = "$password";
        DriverManagerDataSource d = new DriverManagerDataSource(url, username, password);
        d.setDriverClassName("com.mysql.cj.jdbc.Driver");
        return d;
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }
    

    1.2データベース構成
    データベースにテーブルstudentがあるとします.
    CREATE TABLE student (
        id BIGINT(20) NOT NULL COMMENT '  ID',
        name VARCHAR(30) NULL DEFAULT NULL COMMENT '  ',
        age INT(11) NULL DEFAULT NULL COMMENT '  ',
        email VARCHAR(50) NULL DEFAULT NULL COMMENT '  ',
        PRIMARY KEY (id)
    );
    
    INSERT INTO student (id, name, age, email) VALUES
      (1, 'Jack',  28, '[email protected]'),
      (2, 'Louis', 20, '[email protected]'),
      (3, 'Tom',   24, '[email protected]'),
      (4, 'Sandy', 12, '[email protected]'),
      (5, 'Lily',  85, '[email protected]');
    

    対応モデル:
    @Data
    @AllArgsConstructor
    public class Student {
        private Integer id;
        private String name;
        private Integer age;
        private String email;
    }
    

    1.3クエリ文の例
    JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
    jdbcTemplate.query(
        "SELECT * FROM student;",
        (resultSet, i) -> new Student(resultSet.getInt(1), resultSet.getString(2), resultSet.getInt(3), resultSet.getString(4))
    ).forEach(System.out::println);
    

    2.Springトランザクション制御
  • トランザクション制御は一般的にサービス層にあり、「プログラミングトランザクション」と「宣言トランザクション」に分けられる.SringはAOPに基づいて実現される後者を提供する.
  • Spring JDBCベースのトランザクションマネージャはDataSourceTransactionManagerです.

  • 2.1プロファイル
    Springのトランザクション制御はAOPに基づいて実現されるため,AOPのネーミングスペースも導入する必要がある.
    <beans xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="
                http://www.springframework.org/schema/aop
                http://www.springframework.org/schema/aop/spring-aop.xsd
                http://www.springframework.org/schema/tx
                http://www.springframework.org/schema/tx/spring-tx.xsd">
    beans>
    

    2.2取引インタフェースSpring-tx(Spring Transaction)の3つのコアインタフェースは、次のとおりです.
  • PlatformTransactionManager:トランザクションの管理に使用されます.
  • TransactionDefinition:トランザクションを定義するための関連情報.
  • TransactionStatus:トランザクションのステータスを記述します.

  • 2.2.1取引管理:PlatformTransactionManager
  • TransactionStatus getTransaction(TransactionDefinition definition):トランザクションステータス情報を取得します.
  • void commit(TransactionStatus status):トランザクションをコミットします.
  • void rollback(TransactionStatus status):トランザクションをロールバックします.

  • 2.2.2取引情報:TransactionDefinition
  • String getName():トランザクション・オブジェクト名を取得します.
  • int getIsolationLevel():トランザクションの独立性レベルを取得します.
  • int getPropagationBehavior():トランザクションの伝播動作を取得します.
  • int getTimeout():トランザクションのタイムアウト時間を取得します.
  • boolean isReadOnly():トランザクションが読み取り専用かどうかを取得します.

  • ここで、トランザクションの伝播動作は次のように定義されます.
    属性名
    説明
    の値
    PROPAGATION_REQUIRED
    現在トランザクション環境がある場合は、現在実行中のトランザクション環境に追加します.そうでない場合は、新しいトランザクションを作成します.【デフォルト】
    REQUIRED
    PROPAGATION_SUPPORTS
    現在のメソッドを現在のトランザクション環境に追加することを指定します.現在トランザクションがない場合は、非トランザクションで実行します.
    SUPPORTS
    PROPAGATION_MANDATORY
    現在のメソッドが現在のトランザクション環境に追加される必要があることを指定します.現在トランザクションがない場合、例外が放出されます.
    MANDATORY
    PROPAGATION_REQUIRES_NEW
    新しいトランザクションが作成され、現在のメソッドがトランザクションにすでに存在する場合は、現在新しいトランザクションが保留され、実行待ちになります.
    REQUIRES_NEW
    PROPAGATION_NOT_SUPPORTED
    現在のトランザクションはサポートされず、非トランザクション状態で実行されます.トランザクション環境が現在存在する場合は、現在のメソッドが先に実行されるまで保留します.
    NOT_SUPPORTED
    PROPAGATION_NEVER
    現在のトランザクションはサポートされていません.現在のメソッドがトランザクションにある場合は、例外が放出されます.
    NEVER
    PROPAGATION_NESTED
    現在のメソッドの実行時に、すでにトランザクションが存在する場合は、ネストされたトランザクションで実行することを指定します.現在の環境で実行されていないトランザクションの場合は、ロールバック可能な複数のセーブポイントを持つ親トランザクションと独立した新規トランザクションが作成されます.内部トランザクションロールバックは外部トランザクションに影響を与えません(DataSourceTransactionManagerトランザクションマネージャのみ有効).
    NESTED
    2.2.3取引状態:TransactionStatus
  • void flush():トランザクションをリフレッシュします.
  • boolean hasSavepoint():セーブポイントが存在するかどうかを取得します.
  • boolean isCompleted():トランザクションが完了したかどうかを取得します.
  • boolean isNewTransaction():新しいトランザクションかどうかを取得します.
  • boolean isRollbackOnly():ロールバックするかどうかを取得します.
  • void setRollbackOnly():トランザクションロールバックを設定します.

  • 2.3 Springの宣言トランザクション管理
    振替用のデータベース・トランザクション・シーンを構築するには、次のようにします.
    CREATE TABLE account (
        id INT (11) PRIMARY KEY AUTO_INCREMENT COMMENT '  ID',
        username VARCHAR(20) NOT NULL COMMENT '  ',
        money INT DEFAULT NULL COMMENT '    '
    );
    /*      */
    INSERT INTO account VALUES (1, '  ', 2000);
    INSERT INTO account VALUES (2, '  ', 1000);
    

    対応するModelクラス:
    @Data
    @AllArgsConstructor
    public class Account {
        private Integer id;
        private String username;
        private Integer money;
    }
    

    永続レイヤーを発行する
    @Repository(value = "accountDao")
    public class AccountDaoImpl implements AccountDaoInterface{
        private JdbcTemplate jdbcTemplate;
    
        @Autowired
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    
        @Override
        public void send(int money, Account sbPaid) {
            val sql = "UPDATE account SET money=money-? WHERE username=?;";
            this.jdbcTemplate.update(sql, money, sbPaid.getUsername());
        }
    
        @Override
        public void receive(int money, Account sbReceived) {
            val sql = "UPDATE account SET money=money+? WHERE username=?;";
            this.jdbcTemplate.update(sql, money, sbReceived.getUsername());
        }
        
        @Override
        public Account query(Account account){
            val sql = "SELECT * FROM account WHERE username=?";
            return this.jdbcTemplate.query(sql,
                    (res, index) -> new Account(res.getInt(1),
                            res.getString(2),
                            res.getInt(3)
                    ),
                account.getUsername()).get(0);
        }
    }
    

    ビジネスロジック:
    @Service(value = "accountService")
    public class AccountServiceImpl implements AccountServiceInterface {
        private AccountDaoInterface accountDao;
    
        @Autowired
        public void setAccountDao(AccountDaoInterface accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void transfer(Account from, Account to, int money) throws Throwable {
            this.accountDao.send(money, from);
            if(money >= 1000) throw new Exception("      ");
            this.accountDao.receive(money, to);
        }
    
        @Override
        public Account query(Account account){
            return this.accountDao.query(account);
        }
    }
    

    テストクラス:
    AccountServiceInterface accountService = (AccountServiceInterface)context.getBean("accountService");
    System.out.println(accountService.query(new Account(1, "  ", -1)));
    System.out.println(accountService.query(new Account(2, "  ", -1)));
    accountService.transfer(
            new Account(1, "  ", -1),
            new Account(2, "  ", -1), 500); //   1000         
    System.out.println(accountService.query(new Account(1, "  ", -1)));
    System.out.println(accountService.query(new Account(2, "  ", -1)));
    

    2.3.1例①:XMLベースの宣言トランザクション管理
    
    <tx:advice id="txAdvice" transaction-manager="txManager">
      <tx:attributes>
        
        <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.Exception"/>
      tx:attributes>
    tx:advice>
    
    
    <aop:config>
      
      <aop:pointcut expression="execution(* MVC.Service.AccountServiceImpl.transfer(..))" id="txPointCut" />
      
      <aop:advisor pointcut-ref="txPointCut" advice-ref="txAdvice" />
    aop:config>
    
    

    2.3.2 tx:methodのプロパティの詳細
    ツールバーの
    を選択します.
    デフォルト
    説明
    propagation
    propagation列挙
    REQUIRED
    トランザクション伝播プロパティ(詳細は2.2.2を参照)
    isolation
    isolation列挙
    DEFAULT(使用するデータベースのデフォルトレベル)
    トランザクション独立性レベル
    read-only
    boolean
    false
    最適化された読み取り専用トランザクションを使用するかどうか
    timeout
    int
    -1
    タイムアウト(秒)
    rollbackFor
    Class[]
    {}
    ロールバックが必要な例外クラス
    rollbackForClassName
    String[]
    {}
    ロールバックが必要な例外クラス名
    noRollbackFor
    Class[]
    {}
    ロールバック不要の例外クラス
    noRollbackForClassName
    String[]
    {}
    ロールバック不要の例外クラス名
  • read-onlyの値がtrueであれば、読取り専用トランザクション、すなわち接続点内にINSERTなどの書き込み操作がない.
  • 隔離等級は全部で5級:
  • DEFAULT:【デフォルト】データベースのデフォルトの独立性レベルを使用します.
  • SERIALIZABLE:最も厳格なレベルで、トランザクションがシリアルに実行され、リソース消費が最大です.
  • REPEATABLE_READ:1つのトランザクションが、別のトランザクションによって読み込まれたがコミットされていないデータを変更しないことを保証します.汚れた読み取りと重複しない読み取りは回避されますが、パフォーマンスの損失は増加します.
  • READ_COMMITTED:ほとんどの主流データベースのデフォルトのトランザクション・レベルは、1つのトランザクションが別のパラレル・トランザクションが変更されたがコミットされていないデータを読み込まないことを保証し、「汚れた読み取り」を回避します.このレベルは、ほとんどのシステムに適用されます.
  • READ_UNCOMMITTED:読み込み中に不正なデータが読み込まれないことを保証します.独立性レベルは、複数のトランザクションを処理する同時問題です.

  • 2.3.3例②:注釈ベースの宣言トランザクション管理
    コンフィギュレーション・ファイルでトランザクション・ノートのドライバと登録トランザクション・マネージャを開きます.
    
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource" />
    bean>
    
    <tx:annotation-driven transaction-manager="txManager"/>
    

    ビジネス・レベルの変更:
    @Service(value = "accountService")
    //      ,    2.3.1 & 2.3.2      
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = {Exception.class})
    public class AccountServiceImpl implements AccountServiceInterface {
        private AccountDaoInterface accountDao;
    
        @Autowired
        public void setAccountDao(AccountDaoInterface accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void transfer(Account from, Account to, int money) throws Throwable {
            this.accountDao.send(money, from);
            if(money >= 1000) throw new Exception("      ");
            this.accountDao.receive(money, to);
        }
    
        @Override
        public Account query(Account account){
            return this.accountDao.query(account);
        }
    }
    

    付録学習ノート①~③コード及び工程書類
  • (ダウンロード後zipファイルに変更)——springtest.pdf
  • もっと見たい?——SpringBoot、Restful、ファイルアップロード…