ローダーの作成

アプリケーション内でユーザー独自の Loader プラグイン実装を作成することができますが、WebSphere eXtreme Scale の共通プラグイン規則に従う必要があります。

Loader プラグインの組み込み

この Loader インターフェースには、以下の定義があります。

public interface Loader
{
    static final SpecialValue KEY_NOT_FOUND;
    List get(TxID txid, List keyList, boolean forUpdate) throws LoaderException;
    void batchUpdate(TxID txid, LogSequence sequence) throws
				LoaderException, OptimisticCollisionException;
    void preloadMap(Session session, BackingMap backingMap) throws LoaderException;
}

詳しくは、ローダーを参照してください。

get メソッド

バッキング・マップは Loader の get メソッドを呼び出し、keyList 引数として渡されるキー・リストに関連付けられた値を取得します。 get メソッドは、キー・リストにある各キーのうちの 1 つの値の java.lang.util.List リストを戻す必要があります。値リストに戻される最初の値はキー・リストの最初のキーに対応し、 値リストに戻される 2 番目の値はキー・リストの 2 番目のキーに対応し、以降同様になります。 キー・リスト内でキーの値を検出できなかったローダーは、 Loader インターフェースで定義された特別な KEY_NOT_FOUND 値オブジェクト を戻す必要があります。 バッキング・マップは、null を有効な値として許可するよう構成できるので、キーを検出できない Loader が特別な KEY_NOT_FOUND オブジェクトを戻すことが極めて重要になります。この特殊値により、バッキング・マップは null 値とキーが検出できなかったため存在しない値とを区別できます。バックアップ・マップが null 値をサポートしない場合、 存在しないキーについて KEY_NOT_FOUND オブジェクトではなく null 値を戻す Loader は、例外を発生します。

forUpdate 引数は、アプリケーションがマップ上で get メソッドまたは getForUpdate メソッドのいずれを呼び出したかを Loader に通知します。詳しくは、ObjectMap interfaceを 参照してください。ローダーは、永続ストアへの並行アクセスを制御する、並行性制御ポリシーの実装を担当します。 例えば、多くのリレーショナル・データベース管理システムは、リレーショナル・テーブルからデータを読み取るために 使用される SQL SELECT ステートメントの FOR UPDATE 構文をサポートします。 ローダーは、ブール値 true が、このメソッドの forUpdate パラメーターに引数値として渡されるかどうかに基づいて、SQL SELECT ステートメントの FOR UPDATE 構文を使用することを選択できます。 通常、ローダーはペシミスティック並行性の制御ポリシーが使用される場合にのみ FOR UPDATE 構文を使用します。オプティミスティック並行性制御の場合、ローダーは SQL SELECT ステートメントで FOR UPDATE 構文を使用することはありません。ローダーは、そのローダーが使用している並行性制御ポリシーに基づいて forUpdate 引数の使用を判別します。

txid パラメーターの説明については、トランザクションのライフサイクル・イベントの管理のためのプラグインを参照してください。

batchUpdate メソッド

batchUpdate メソッドは、Loader インターフェースにおいて重要です。eXtreme Scale によって現在のすべての変更が Loader に適用される必要がある場合、必ずこのメソッドが呼び出されます。 ローダーには、選択されたマップの変更のリストが与えられます。変更は繰り返され、バックエンドに適用されます。 このメソッドは現行の TxID 値および適用する変更を受け取ります。 以下のサンプルは、一連の変更に対して繰り返し適応され、3 つの Java Database Connectivity (JDBC) ステートメント (INSERT、UPDATE、および DELETE) をバッチ処理します。

import java.util.Collection;
import java.util.Map;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.ibm.websphere.objectgrid.TxID;
import com.ibm.websphere.objectgrid.plugins.Loader;
import com.ibm.websphere.objectgrid.plugins.LoaderException;
import com.ibm.websphere.objectgrid.plugins.LogElement;
import com.ibm.websphere.objectgrid.plugins.LogSequence;

    public void batchUpdate(TxID tx, LogSequence sequence) throws LoaderException {
        // Get a SQL connection to use.
        Connection conn = getConnection(tx);
        try {
            // Process the list of changes and build a set of prepared
            // statements for executing a batch update, insert, or delete
            // SQL operation.
            Iterator iter = sequence.getPendingChanges();
            while (iter.hasNext()) {
                LogElement logElement = (LogElement) iter.next();
                Object key = logElement.getKey();
                Object value = logElement.getCurrentValue();
                switch (logElement.getType().getCode()) {
                case LogElement.CODE_INSERT:
                    buildBatchSQLInsert(tx, key, value, conn);
                    break;
                case LogElement.CODE_UPDATE:
                    buildBatchSQLUpdate(tx, key, value, conn);
                    break;
                case LogElement.CODE_DELETE:
                    buildBatchSQLDelete(tx, key, conn);
                    break;
                }
            }
            // Execute the batch statements that were built by above loop.
            Collection statements = getPreparedStatementCollection(tx, conn);
            iter = statements.iterator();
            while (iter.hasNext()) {
                PreparedStatement pstmt = (PreparedStatement) iter.next();
                pstmt.executeBatch();
            }
        } catch (SQLException e) {
            LoaderException ex = new LoaderException(e);
            throw ex;
        }
    }
前のサンプルは、LogSequence 引数の処理の高水準ロジックを示していますが、 SQL の INSERT、UPDATE、または DELETE ステートメントがビルドされる方法の 詳細については示されていません。 示されているキーポイントには、以下のようなものがあります。
  • getPendingChanges メソッドは、LogSequence 引数で呼び出され、ローダーが処理を必要とする LogElements のリストのイテレーターを取得します。
  • LogElement.getType().getCode() メソッドを使用して、LogElement が SQL の INSERT、UPDATE、または DELETE 操作用であるかどうかを判断します。
  • SQLException 例外はキャッチされ、 バッチ更新中に発生した例外を報告するために発行される LoaderException 例外にチェーンされます。
  • JDBC バッチ更新サポートは、作成する必要のあるバックエンドへの照会の数を最小化するために使用されます。

preloadMap メソッド

eXtreme Scale の初期化中に、定義された各バックアップ・マップが初期化されます。Loader がバッキング・マップにプラグインされると、バッキング・マップは Loader インターフェースで preloadMap メソッドを呼び出し、 ローダーがバックエンドからデータをプリフェッチし、マップにデータをロードできるようにします。以下のサンプルでは、Employee テーブルの最初の 100 行がデータベースから 読み取られて、マップにロードされると仮定します。 EmployeeRecord クラスはアプリケーションが提供するクラスであり、 従業員テーブルから読み取った従業員データを保持します。

注: このサンプルは、すべてのデータをデータベースからフェッチし、それを 1 つの区画のベース・マップへ挿入します。 実際の分散 eXtreme Scale デプロイメントのシナリオでは、データはすべての区画に配布されなければなりません。 詳しくは、クライアント・ベースの JPA ローダーの開発を参照してください。
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.ibm.websphere.objectgrid.Session;
import com.ibm.websphere.objectgrid.TxID;
import com.ibm.websphere.objectgrid.plugins.Loader;
import com.ibm.websphere.objectgrid.plugins.LoaderException

    public void preloadMap(Session session, BackingMap backingMap) throws LoaderException {
        boolean tranActive = false;
        ResultSet results = null;
        Statement stmt = null;
        Connection conn = null;
        try {
            session.beginNoWriteThrough();
            tranActive = true;
            ObjectMap map = session.getMap(backingMap.getName());
            TxID tx = session.getTxID();
            // Get a auto−commit connection to use that is set to
            // a read committed isolation level.
            conn = getAutoCommitConnection(tx);
            // Preload the Employee Map with EmployeeRecord
            // objects. Read all Employees from table, but
            // limit preload to first 100 rows.
            stmt = conn.createStatement();
            results = stmt.executeQuery(SELECT_ALL);
            int rows = 0;
            while (results.next() && rows < 100) {
                int key = results.getInt(EMPNO_INDEX);
                EmployeeRecord emp = new EmployeeRecord(key);
                emp.setLastName(results.getString(LASTNAME_INDEX));
                emp.setFirstName(results.getString(FIRSTNAME_INDEX));
                emp.setDepartmentName(results.getString(DEPTNAME_INDEX));
                emp.updateSequenceNumber(results.getLong(SEQNO_INDEX));
                emp.setManagerNumber(results.getInt(MGRNO_INDEX));
                map.put(new Integer(key), emp);
                ++rows;
            }
            // Commit the transaction.
            session.commit();
            tranActive = false;
        } catch (Throwable t) {
            throw new LoaderException("preload failure: " + t, t);
        } finally {
            if (tranActive) {
                try {
                    session.rollback();
                } catch (Throwable t2) {
                    // Tolerate any rollback failures and
                    // allow original Throwable to be thrown.
                }
            }
            // Be sure to clean up other databases resources here
            // as well such a closing statements, result sets, etc.
        }
    }
このサンプルは以下のキーポイントを示します。
  • preloadMap のバックアップ・マップはセッション引数として渡されるセッション・オブジェクトを使用します。
  • Session.beginNoWriteThrough メソッドを使用して、begin メソッドの代わりにトランザクションを開始します。
  • マップのロードに関してこのメソッドで発生する各 put 操作にローダーを呼び出すことはできません。
  • ローダーは、従業員テーブルの列 を EmployeeRecord Java オブジェクトのフィールドにマップすることができます。 ローダーは、発生したすべてのスロー可能な例外をキャッチし、 LoaderException 例外を、その例外にチェーンされているキャッチしたスロー可能な例外と一緒にスローします。
  • finally ブロックにより、beginNoWriteThrough メソッドが呼び出される時点から commit メソッドが呼び出される時点までの間に発生するすべてのスロー可能な例外は、確実に finally ブロックにアクティブなトランザクションをロールバックします。このアクションは、preloadMap メソッドによって開始されたすべてのトランザクションが、 呼び出し側に戻される前に確実に完了させるために、重要です。 finally ブロックは、Java Database Connectivity (JDBC) 接続やその他の JDBC オブジェクトのクローズのような、必要とされる可能性のあるそれ以外のクリーンアップ・アクションを行う場所としても適切です。

preloadMap サンプルは、テーブルの行をすべて選択する SQL SELECT ステートメントを使用しています。 アプリケーションが提供する Loader では、マップにプリロードするテーブルの数を制御するために、 1 つ以上の Loader プロパティーを設定します。

preloadMap メソッドは BackingMap の初期化中に 1 回しか呼び出されないので、 1 回だけのローダー初期化コードの実行場所としても適切です。 ローダーがバックエンドからデータをプリフェッチせず、データをマップにロードしないことを選択した場合であっても、それ以外に何らかの 1 回だけの初期化を実行し、さらに効率的なローダーの別のメソッドを作成する必要があると考えられます。以下は、TransactionCallback オブジェクトおよび OptimisticCallback オブジェクト を Loader のインスタンス変数としてキャッシングして、Loader の別のメソッドが これらのオブジェクトにアクセスするためにメソッド呼び出しを行わなくても済むようにする例です。 BackingMap の初期化後に、TransactionCallback オブジェクトおよび OptimisticCallback オブジェクト を変更または置換できなくなるため、この ObjectGrid プラグインの値のキャッシングを行うことが可能です。 これらのオブジェクト参照をローダーのインスタンス変数としてキャッシュに入れることは許容されます。

import com.ibm.websphere.objectgrid.Session;
import com.ibm.websphere.objectgrid.BackingMap;
import com.ibm.websphere.objectgrid.plugins.OptimisticCallback;
import com.ibm.websphere.objectgrid.plugins.TransactionCallback;

    // Loader instance variables.
    MyTransactionCallback ivTcb; // MyTransactionCallback

    // extends TransactionCallback
    MyOptimisticCallback ivOcb; // MyOptimisticCallback

    // implements OptimisticCallback
    // ...
    public void preloadMap(Session session, BackingMap backingMap) throws LoaderException
		 [Replication programming]
        // Cache TransactionCallback and OptimisticCallback objects
        // in instance variables of this Loader.
        ivTcb = (MyTransactionCallback) session.getObjectGrid().getTransactionCallback();
        ivOcb = (MyOptimisticCallback) backingMap.getOptimisticCallback();
        // The remainder of preloadMap code (such as shown in prior example).
    }

レプリカ生成フェイルオーバーに関係するプリロードおよび回復可能なプリロードについて詳しくは、可用性向上のためのレプリカ生成製品概要でレプリカ生成に関する説明を参照してください。

エンティティー・マップが設定されたローダー

ローダーがエンティティー・マップにプラグインされている場合は、ローダーでタプル・オブジェクトを処理する必要があります。タプル・オブジェクトは特別なエンティティー・データ・フォーマットです。ローダーでは、タプルとその他のデータ・フォーマット間でデータ変換を実行する必要があります。例えば、get メソッドにより、このメソッドに渡されるキーのセットに対応する値のリストが返されます。渡されたキーは Tuple のタイプに置かれ、キー・タプルと呼ばれます。ローダーが JDBC を使用しているデータベースでマップをパーシストすると想定した場合、get メソッドは、各キー・タプルをエンティティー・マップにマップされているテーブルの 1 次キーの列に対応する属性値リストに変換し、データベースからデータをフェッチする基準として変換された属性値を使用する WHERE 文節が含まれている SELECT ステートメントを実行した後、返されたデータを値タプルに変換する必要があります。get メソッドは、データベースからデータを取得し、渡されたキー・タプルに対する値タプルにそのデータを変換した後、呼び出し元に渡されたタプル・キーのセットに対応する値タプルのリストを返します。get メソッドは 1 つの SELECT ステートメントを実行して一度にすべてのデータをフェッチするか、または各キー・タプルに対して SELECT ステートメントを実行します。データがエンティティー・マネージャーを使用して保管されるときにローダーを どのように使用するのかを示すプログラミングの詳細は、エンティティー・マップおよびタプルとのローダーの使用を参照してください。