データベース接続のリリースに関する設計モード


Javaを使ってビジネスアプリケーションを開発した人はきっとデータベースを使ったことがあります.
具体的な方案はJDBCを使ってもJDOを使っても、直接J 2 EEから提供された接続池を使っても、あるいはHbernateを使ってこのすべてを遮断しても、不変の原則は、データベース接続は限られた資源であり、データの持続化の効率と安定を実現するために、制御せずに頻繁にデータベース接続を作成するべきではない.更に接続を確立し(または接続池から取り出し)、使用後に簡単に終了してはいけません.資源の回収をしません.このような資源には時間外の自動回収機構があっても、このようなやり方は受け入れられない.
しっかりして、適当にコメントをしました.さて、本文のタイトルはデータベース接続のリリースについて議論していますが、この話題を借りて、デザインモードのJavaでの応用を説明しています.
現在のプロジェクトのデータベース接続方式はJDBC+接続池を採用しています.このように考えられるのは、おそらくこの実現の効率が一番高く、発揮できるところも一番多いからです.
システム起動段階にデータベースの接続池を作りました.中にはたくさんの接続が置いてあります.需給を持続化するモジュールの呼び出しと返却の接続を受け取ります.
ここでは、各ブロックが接続コードを借りています.使い終わったら必ず接続を返却してください.そうでなければ、接続池の接続がなくなりましたら、システムは新しい接続を作らなければなりません.遅かれ早かれ接続ができない異常を投げ出します.
最初のスキームは、すべてのデータベース操作をtryブロックに入れます.その後finallyで接続を返却する操作を行います.この案の利点は実現しやすく,目的は単純である.一つ、二つのデータベース操作だけの応用にとって、効果は明らかで、維持も難しくないです.
しかし、仮にアプリケーションには一つのデータベースだけが含まれていないとしても、例えば機能によって安全管理、商品在庫、販売記録、社員勤務評定などの倉庫(表スペース)を区分しています.特に、いくつかの倉庫を訪問します.例えば、商品の在庫と販売記録を同時に訪問して、安全管理と社員の勤務評定に訪問します.この時、プログラマはいつもどのような接続が使われていますか?一番簡単な例をあげます.

// ...
Connection connA = DbUtil.getConnA();
Connection connB = DbUtil.getConnB();

try {
    // ...
} catch (Exception ex) {
    // ...
} finally {
    connA.close();
    connB.close();
}
太いように見えますが、二つの接続が全部解けたようです.しかし、実際にはそうではありません.connBが消されるとは限りません.tryブロックの中で、connAのサーバが急に起動したと仮定します.この時、finallyブロックの中で、connA.close()異常を投げます.connB.close()コードは実行されません.
この時、専門家が話しています.finallyブロックの中に各close()方法にtryブロックをセットすればいいです.

        // ...
    } finally {
        try {
            connA.close();
        } catch (Exception ex) {
        }
        try {
            connB.close();
        } catch (Exception ex) {
        }
    }
}
このように直すのは問題ないですが、少し長いです.もう一度修正します

        // ...
    } finally {
        DbUtil.close(connA);
        DbUtil.close(connB);
    }
}
// ...
public final class DbUtil
{
    private DbUtil()
    {
    }

    public static void close(Connection conn)
    {
        try {
            conn.close();
        } catch (Exception ex) {
        }
    }
}
どのように何十個の接続場所を保証しますか?特にC+Pの後、コンパイルもロジックを説明しないで大丈夫です.たとえば:

        // ...
    } finally {
        DbUtil.close(connA);
        DbUtil.close(connA); // C + P    ,        
    }
}
// ...
何を使ったらいいのかを覚えてリリースするということは、全く安心感がありません.
では、今はデザインの旗を盛大に祭ります.
でも、すみませんが、具体的にどんなパターンですか?実は私の心にも底がありません.先に説明します.
まず、設計の目的は、ある種の資源を比較的に自由に利用できるようにすることです.そして、資源の使用を停止した後、必ず自動的に資源の放出を行うことです.
第二に、設計思想は、資源を使うこの操作を全体の操作の中で取り替えることができる部分に変えて、全体の流れはこのようなものであるべきです.資源を取って、資源を使って、資源を釈放します.その中で資源の使用は絶えず変化しています.資源の取得に応じて変化もありますが、資源の放出には変化がなく、しかもこのような3つのステップの流れもずっと変わらないです.
したがって、オブジェクトがあると考えられ、作成時にリソースとリソースの操作をパラメータとして注入し、3ステップ実行します.このように、すべてのデータベース操作がこのオブジェクトを使用して実行される限り、すべてのリソースは必ず正しいリリースされると確信しています.私たちが設計した対象に他の欠陥がない限り.しかし、このようなスキームは、コードのメンテナンスを大幅に簡略化し、各所に散布されているデータの持続化操作のメンテナンスをある種類のメンテナンスに移行させることができます.
Javaはクローズドされていないので、オブジェクト模倣関数ポインタしか使用できません.コードは以下の通りです

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * DbAdapter
 *         
 */
public abstract class DbAdapter
{
    /**
     *   sql,              
     */
    static Object process(Connection conn, String sql, DbParamInjector injector, DbReader reader) throws SQLException
    {
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = conn.prepareStatement(sql);
            if (injector != null) {
                if (reader != null) {
                    injector.setParams(pstmt);
                    rs = pstmt.executeQuery();
                    return reader.read(rs);
                } else {
                    do {
                        injector.setParams(pstmt);
                        pstmt.execute();
                    } while (injector.hasNext());
                    return null;
                }
            } else {
                if (reader != null) {
                    rs = pstmt.executeQuery();
                    return reader.read(rs);
                } else {
                    pstmt.execute();
                    return null;
                }
            }
        } finally {
            //        
            DBUtil.close(conn, pstmt, rs);
        }
    }
}

/**
 * DbParamInjector
 * PreparedStatement      
 */
abstract class DbParamInjector
{
    abstract void setParams(PreparedStatement pstmt) throws SQLException;

    /**
     *                ,             
     */
    boolean hasNext()
    {
        return false;
    }
}

/**
 * DbReader
 *          
 */
interface DbReader
{
    Object read(ResultSet rs) throws SQLException;
}
使用例は以下の通りです.

// ...
String sql = "INSERT INTO TBL_USERLOG (LOGID, USERID, LOGDATE, LOGNOTE)"
    +" VALUES (?, ?, ?, ?)";
DbParamInjector injector = new DbParamInjector() {

    public void setParams(PreparedStatement pstmt) throws SQLException
    {
        int i = 1;
        pstmt.setInt(i++, logId);
        pstmt.setInt(i++, userId);
        pstmt.setTimestamp(i++, new Timestamp(new Date().getTime()));
        pstmt.setString(i++, new logNote);
    }
};
try {
    DbAdapter.process(conn, sql, injector, null);
} catch (SQLException ex) {
    ex.printStackTrace();
}
// ...
もちろん、上記のコードはあるデータベースへのアクセスの必要性に合致するだけで、一般的に適用される例ではありません.データを読み込みながらテーブルまたは他のより複雑なモードのデータベースを更新する必要がある場合は、パラメータを導入する関数のオブジェクトとしてのデザインと実装をさらに変更する必要があります.ここはもうこれ以上展開しません.
OKです.最後にその中のデザインについて説明します.
まず、コマンドモードとは、
  • 私はプログラミング項目を受けます.これは抽象概念です.
  • のすべてのプログラム項目は同じ需要文書形式であり、同じ製品出力媒体であり、これは統一インターフェースである.
  • Javaプロジェクトを実現する必要があります.これは具体的な要求です.
  • そこで、私は小さい店員を雇って、プロジェクトを作ってあげました.お金をください.これは一例です.
  • このように、私は対外的にプログラミングプロジェクトを受け入れますが、実質的な仕事は私がやったのではなく、仕事を他の人に転嫁してやります.
    私はただプログラミング人員の需要を規範化しただけです.このインターフェースを通してプロジェクトを配布します.
    プログラマがどのように開発されているかは分かりません.
    私達の例では、小僧さんが使っているデマンド文書のフォーマットはDbParam Injectorです.出力があれば、処理出力はDbReaderです.
    第二に、工場モデルとは、
  • コーヒーメーカーが缶コーヒーを作っています.
  • 缶のコーヒーにはコーヒー粉、脂末、砂糖、添加剤、瓶、包装紙が含まれています.
  • 瓶詰めの流れは決まっています.原料はまだ来ていません.
  • 原料が決まったら、コーヒーの風味が決まります.
  • でしたら、まず席を決めて、みんなで並んで座ります.
  • 原料が来ました.缶コーヒーも出てきました.
  • 私たちの例では、最後のステップが多くなりました.残りのコーヒー粉を掃き上げて、原料プールに入れます.