ThreadLocalの使用からSpringのトランザクション管理へ


私がこの文章を書く目的は、皆さんが事務の法則をよりよく理解し、理解できるように、初心者が事務の内容を学ぶのに役立つことを望んでいます.私たちがアプリケーションを開発するとき、多くの場合、私たちのビジネス操作はデータベースを複数回操作します.時には、このような一連の操作がすべて成功するか、すべて失敗するかを保証する必要があります.実は、この概念は私たちが今日議論するトランザクションです.現在、アプリケーションを開発するには一般的に3つの階層構造が採用されています.トランザクションを制御するコードがDAO(DataAccessObject)オブジェクトに配置され、DAOオブジェクトの各メソッドでトランザクションを開いたり閉じたりします.サービスオブジェクトがDAOを呼び出している間に、DAOを1つだけ呼び出すと、効果的です.しかし、私たちのサービスは一連のDAOを呼び出してデータベースを複数回操作することがよくあります.この場合、トランザクションの境界を制御することはできません.実際のアプリケーションでは、私たちのサービス呼び出しのDAOの個数は不確定で、必要に応じて変化することができ、サービス呼び出しの状況も発生する可能性があります.手動でトランザクションを制御することは、少し厳格なシステムにとって完全に現実的ではないようです.では、今私たちは何か良い解決策がありますか?EJBの自慢の声明式事務を覚えていますか.今は徐々に没落していますが、その思想は後世に吸収されています.私たちのSpringフレームワークは軽量級のフレームワークで、声明式事務のサポートを実現しています.私たちは構成と挿入可能な方法でアプリケーション全体の事務の管理を完成することができます.Sping事務については、今日お話しするのはThreadLocalで、JDK 1.2のバージョンでjavaを提供しています.lang.ThreadLocal,ThreadLocalはマルチスレッドプログラムの同時問題を解決するために新しい考え方を提供した.簡単に言えば、ThreadLocalはスレッドごとに変数を保存し、各スレッドは自分の対応する変数にアクセスするので、synchronizedキーワードを使わずにスレッド同期を実現することができます.ThreadLocalの詳細については、http://hi.baidu.com/cjjic02/blog/item/1ba41813aabde8886438dbe5.htmlを参照してください.やはり手作業でThreadLocalで接続を管理して、くだらないことは言わないで、まずコードTransactionHelperを見てみましょう
package com.hwadee.demo;  
  
import java.io.IOException;  
import java.io.InputStream;  
import java.sql.Connection;  
import java.sql.DriverManager;  
import java.sql.SQLException;  
import java.util.Properties;  
  
public final class TransactionHelper {  
      
    //  ThreadLocal              
    private final static ThreadLocal<Connection> connection_holder = new ThreadLocal<Connection>();  
      
    //    ,  connection.properties  
    private final static Properties connectionProp = new Properties();  
      
    static{       
        //        
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("connection.properties");  
        try {  
              
            connectionProp.load(is);  
            is.close();  
            //        
            Class.forName(connectionProp.getProperty("driverClassName"));  
        } catch (IOException e) {  
             throw new RuntimeException(e.getMessage(),e);  
        }catch(ClassNotFoundException e){  
            throw new RuntimeException("     ",e);  
        }  
    }  
      
    //               
    private static Connection getCurrentConnection()  
    {  
        Connection conn = connection_holder.get();  
        if(conn == null){  
            conn =  createNotAutoCommitConnection();              
            connection_holder.set(conn);  
        }  
        return conn;  
    }  
      
    //  SQL    
    public static int executeNonQuery(String sql) throws SQLException{  
          
        Connection conn = getCurrentConnection();  
           
        return conn.createStatement().executeUpdate(sql);  
  
    }  
      
    //      
    public static void commit(){  
        Connection conn = getCurrentConnection();  
        try {  
            conn.commit();  
            conn.close();  
            connection_holder.set(null);  
        } catch (SQLException e) {  
            throw new RuntimeException(e.getMessage(),e);  
        }  
    }  
      
      
    //      
    public static void rollback(){  
        Connection conn = getCurrentConnection();  
        try {  
            conn.rollback();  
            conn.close();  
            connection_holder.set(null);  
        } catch (SQLException e) {  
            throw new RuntimeException(e.getMessage(),e);  
        }  
    }  
      
    //       Commit        
    private static Connection createNotAutoCommitConnection() {  
        try {  
              
            Connection conn = DriverManager.getConnection(connectionProp.getProperty("url")+";databaseName="+ connectionProp.getProperty("databaseName")  
                    ,connectionProp.getProperty("username")  
                    ,connectionProp.getProperty("password"));  
            conn.setAutoCommit(false);  
            return conn;  
        } catch (SQLException e) {  
             throw new RuntimeException(e.getMessage(),e);  
        }  
    }     
}  

 
このクラスは基本的な接続管理とSQL文の実行方法を実現し、マルチスレッド環境でプログラムエントリを実行することができる.
package com.hwadee.demo;  
  
import java.sql.SQLException;  
  
public class MainModule {  
      
    public static void main(String[] args) {          
        try{  
              
            insert1();  
              
            insert2();  
              
            //  1 2    ,    ,                  。  
            TransactionHelper.commit();  
        }catch(SQLException e){           
            TransactionHelper.rollback();  
            throw new RuntimeException(e.getMessage(),e);  
        }catch(RuntimeException e){            
            TransactionHelper.rollback();  
            throw new RuntimeException(e.getMessage(),e);  
        }  
    }  
      
      
    static void insert1() throws SQLException{        
        String sql = "insert into department values(1,'   ')";  
          
        TransactionHelper.executeNonQuery(sql);        
    }  
      
    static void insert2() throws SQLException{        
        String sql = "insert into department values(2,'   ')";  
          
        TransactionHelper.executeNonQuery(sql);   
          
        //throw new RuntimeException("  ");       
    }  
}  

接続文字列の構成、このファイルをclasspathルートディレクトリconnectionに入れてください.properties 
url=jdbc:sqlserver://localhost:1433  
databaseName=pubs  
username=sa  
password=password  
driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver  

テーブル文
USE [pubs]  
go  
CREATE TABLE [Department](  
    [DEPT_ID] [int] primary key,  
    [DEPT_NAME] [varchar](50)  
)  
GO  

では、このアプリケーションを実行して、正常に2つのデータを挿入することができます.次に、insert 2メソッドのコメントをキャンセルして、効果を見てみましょう. 
static void insert2() throws SQLException{        
        String sql = "insert into department values(2,'   ')";  
          
        TransactionHelper.executeNonQuery(sql);   
          
        throw new RuntimeException("  ");         
    }  

重要なのは、トランザクションを実装するには、これらの文を同じデータベース接続で実行する必要があります.最終的には、コミットとロールバックを統一する必要があります.このようにinsert 1とinsert 2が異なるDAOであると仮定する方法を注意深く観察することができ,我々のinsert 1とinsert 2は接続を開くことと閉じることを担当していない.間接的に呼び出すのですexecuteNonQuery(sql);これにより、同じ接続を使用してデータベース操作を行うすべての方法が実行されます.実はこの例は声明式トランザクションの一部を実現することを伝えたいだけで、この例は簡単な単一トランザクションモデルしか実現できません.ネストなどのより複雑なトランザクション伝播モデルを実現するには、AOPなどのより多くの技術を使用する必要があります.まずここまで書いて、みんなに役に立つことを望みます!