Db2 11.1

JDBC アプリケーションでのオプティミスティック・ロック

データ・ソースのオプティミスティック・ロックを利用する JDBC アプリケーションを作成することができます。

オプティミスティック・ロック とは、アプリケーションで SELECT 操作と UPDATE または DELETE 操作との間のロック解除に使用できる手法のことです。 選択した行がそのアプリケーションによって更新または削除される前に変更された場合は、UPDATE または DELETE 操作は失敗します。 オプティミスティック・ロックによって、指定されたリソースが他のトランザクションによって使用できない時間が最小限に抑えられます。

Db2® for IBM® i データ・ソースへの接続でオプティミスティック・ロックを使用するには、Db2 for IBM i V6R1 以降が必要です。

通常、アプリケーションでは以下のステップが実行されて、オプティミスティック・ロックが使用されます。

  1. 行を表から選択する。
  2. 表に対してロック解除する。
  3. 選択した行が変更されていない場合は、更新する。

行が変更されたかどうかを検査するために、アプリケーションにより行の変更トークンが照会されます。 行の変更トークンは、必ずしも行が変更されたかどうかの完全に正確な指標であるわけではありません。 行の変更トークンは、行の変更タイム・スタンプ列を持つ表を作成した場合は完全に正確です。 行の変更タイム・スタンプ列なしで表を作成するか、または表を変更して行の変更タイム・スタンプ列を追加した場合は、行の変更トークンに、行に対する更新が正確に反映されていないおそれがあります。 つまり、行の変更トークンにより、行が変更されていないにもかかわらず行が変更されたと示されることがあります。 この状態を false negative 状態と呼びます。

オプティミスティック・ロックを実行するための JDBC アプリケーションを作成する場合は、以下の同様の手順に従います。

  1. 照会を準備して実行する。

    オプティミスティック・ロック情報が必要かどうか、およびその情報に false negative を含めることができるかどうかを指示します。

  2. ResultSet にオプティミスティック・ロック情報が含まれるかどうか、およびその情報で false negative の生成が可能かどうかを判別する。

    オプティミスティック・ロック情報のタイプに基づいて、オプティミスティック・ロックを続行するかどうかを判定することができます。

  3. 表に対してロック解除する。
  4. 選択した行が行の変更トークンによって変更されていないと示される場合は、更新する。

以下のコードでは、JDBC アプリケーションでのオプティミスティック・ロックの実行方法を示しています。 例内の数字は、上記の各手順に対応しています。

com.ibm.db2.jcc.DB2Statement s1 = 
  (com.ibm.db2.jcc.DB2Statement)conn.createStatement();
ResultSet rs = 
 ((com.ibm.db2.jcc.DB2Statement)s1).executeDB2OptimisticLockingQuery 
 ("SELECT EMPNO, SALARY FROM EMP WHERE EMP.LASTNAME = 'HAAS'", 
  com.ibm.db2.jcc.DB2Statement.RETURN_OPTLOCK_COLUMN_NO_FALSE_NEGATIVES);  1 
                                   // Indicate that you plan to do
                                   // optimistic locking, and that you
                                   // want optimistic locking information
                                   // that does not generate
                                   // false negatives
ResultSetMetaData rsmd = rs.getMetaData();
int optColumns =                                                           2 
  ((com.ibm.db2.jcc.DB2ResultSetMetaData)rsmd).getDB2OptimisticLockingColumns();
                                   // Retrieve the optimistic locking 
                                   // information.
boolean optColumnsReturned = false;

if (optColumns == 0);              // If optimistic locking information is not
                                   // returned, do not attempt to do 
                                   // optimistic locking.
else if (optColumns == 1);         // A value of 1 is never returned if  
                                   // RETURN_OPTLOCK_COLUMN_NO_FALSE_NEGATIVES
                                   // is specified, because 1 indicates 
                                   // that there could be false negatives.
else if (optColumns == 2)          // If optimistic locking information is 
   optColumnsReturned = true;      // returned, and false negatives will not
                                   // occur, try optimistic locking.

rs.next();                         // Retrieve the contents of the ResultSet
int emp_id = rs.getInt(1);
double salary = rs.getDouble(2);
 
long rowChangeToken = 0;
Object rid = null;
int type = -1;

if (optColumnsReturned) {          
  rowChangeToken =                 // Get the row change token.
    ((com.ibm.db2.jcc.DB2ResultSet)rs).getDB2RowChangeToken();
  rid = ((com.ibm.db2.jcc.DB2ResultSet)rs).getDB2RID();
                                   // Get the RID, which uniquely identifies
                                   // the row.
  int type = ((com.ibm.db2.jcc.DB2ResultSet)rs).getDB2RIDType ();
                                   // Get the data type of the RID.
}   
// ***************************************************
// Release the locks or disconnect from the database. 
// Perform some work on the retrieved data.
// Reconnect to the data source.
// ***************************************************
…
PreparedStatement s2 = 
  conn.prepareStatement ("UPDATE EMP SET SALARY = ? " +
  "WHERE EMPNO = ? AND ROW CHANGE TOKEN FOR EMP = ? and " +
  "RID_BIT(EMP) = ?");
                                   // Statement for updating the
                                   // previously selected rows that
                                   // have not changed.
s2.setDouble(1, salary+10000);
s2.setInt(2, emp_id);
                                   // Set the new row values.
s2.setLong(3, rowChangeToken);
                                   // Set the row change token of the
                                   // previously retrieved row.
if (type == java.sql.Types.BIGINT)
  s2.setLong (4, ((Long)rid).longValue());
else if (type == java.sql.Types.VARBINARY)
  s2.setBytes (4, (byte[])rid);
                                   // Set the RID of the previously
                                   // retrieved row.
                                   // Use the correct setXXX method
                                   // for the data type of the RID.
int updateCount = s2.executeUpdate();                                       3 
                                   // Perform the update.
if (updateCount == 1);             // Update is successful.
else                               // Update failed.
…