Image
Top
Navigation

トランザクションを利用する その3

はじめに

OrientDB ではトランザクショ内でのロックの機構として Optimistic LockPesmistic Lock の二種類を用意されています。
ただ、バージョン1.7.8で利用できるのはOptimistic Lockのみです。

Optimistic Lock を RDBで実装するとなると監査項目として更新日時を各レコードに持たせ、更新時に一致していないようなら例外を出すみたいな実装をしたことがある人もいるでしょう。そう、結構面倒なんですよね。

Optimistic Lock

OrientDB はデータベース自体でバージョン情報を保持しているため簡単に利用が可能です。

次のサンプルはトランザクション内でアップデート後に commit する前にデータを更新されてしまうといったサンプルです。
その2 を未読の方への補足となりますが Remote 接続時に OCommandSQL で実行されるSQLはトランザクションの管理外で動作されてしまいます。
その挙動を使用したサンプルとなります。

ODatabaseDocumentTx の begin()メソッドの引数に TXTYPE.OPTIMISTIC を指定します。
ちなみにですが、引数をしてしない場合でもデフォルトは TXTYPE.OPTIMISTIC が指定されています。

package transaction;

import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.orientechnologies.orient.core.tx.OTransaction.TXTYPE;

public class Transaction_lock4wp2 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {

            // remoteでデータベースへ接続します。
            ODatabaseDocumentTx db = new ODatabaseDocumentTx("remote:172.16.251.205/DocumentTest");
            db.open("root", "root");

            // TXTYPE.OPTIMISTIC Lockを指定
            db.begin(TXTYPE.OPTIMISTIC);

            // RIDを指定して直接取得
            ODocument doc_ = db.load(new ORecordId("#24:4")).getRecord();

            // save前に値を確認
            System.out.println("> save前に値を確認");
            for (String fieldName : doc_.fieldNames()) {
                System.out.println(fieldName + " : " + doc_.field(fieldName));
            }

            // number を 5 へ更新します
            doc_.field("number", 5);
            doc_.save();

            // save 後の値を確認
            System.out.println("> save 後の値を確認");
            for (String fieldName : doc_.fieldNames()) {
                System.out.println(fieldName + " : " + doc_.field(fieldName));
            }

            // SQLを実行(トランザクションの管理外からの更新)
            String updateQuery = "UPDATE SampleClass set number = 1111 WHERE @RID = '#24:4' ";

            // number を 1111 へ更新します
            db.command(new OCommandSQL(updateQuery)).execute();

            // commit 前に値を確認
            System.out.println("> commit 前に値を確認");
            for (String fieldName : doc_.fieldNames()) {
                System.out.println(fieldName + " : " + doc_.field(fieldName));
            }

            db.commit();

            db.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

このサンプルでは、commit実行時にOConcurrentModificationException が投げられます。

com.orientechnologies.orient.core.exception.OConcurrentModificationException: Cannot UPDATE the record #24:4 because the version is not the latest. Probably you are updating an old record or it has been modified by another user (db=v9 your=v8)
    at com.orientechnologies.orient.core.storage.impl.local.paginated.OLocalPaginatedStorage.updateRecord(OLocalPaginatedStorage.java:795)
    at com.orientechnologies.orient.core.storage.impl.local.paginated.OLocalPaginatedStorage.commitEntry(OLocalPaginatedStorage.java:2122)
    at com.orientechnologies.orient.core.storage.impl.local.paginated.OLocalPaginatedStorage.commit(OLocalPaginatedStorage.java:1076)
    at com.orientechnologies.orient.core.tx.OTransactionOptimistic.doCommit(OTransactionOptimistic.java:464)
    at com.orientechnologies.orient.core.tx.OTransactionOptimistic.commit(OTransactionOptimistic.java:148)
    at com.orientechnologies.orient.core.db.record.ODatabaseRecordTx.commit(ODatabaseRecordTx.java:142)
    at com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx.commit(ODatabaseDocumentTx.java:511)
    at com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx.commit(ODatabaseDocumentTx.java:503)
    at com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.commit(ONetworkProtocolBinary.java:1096)
    at com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.executeRequest(ONetworkProtocolBinary.java:344)
    at com.orientechnologies.orient.server.network.protocol.binary.OBinaryNetworkProtocolAbstract.execute(OBinaryNetworkProtocolAbstract.java:169)
    at com.orientechnologies.common.thread.OSoftThread.run(OSoftThread.java:45)

ただし、先にnumberを5に更新しようとしたが Commit 前にトランザクション外から number を 1111 に更新されてしまったため、Commit時に例外が発生したという形になります。
コンソールから値を確認すると number は 1111 に更新されています。

orientdb {DocumentTest}> SELECT FROM SampleClass WHERE @RID=#24:4
----+-----+------------+----
#   |@RID |name        |id
----+-----+------------+----
0   |#24:4|tran-sample2|1111
----+-----+------------+----
1 item(s) found. Query executed in 0.005 sec(s).

Pessimistic Lock

ODatabaseDocumentTx の begin()メソッドの引数に TXTYPE.PESSIMISTIC を指定します。


            // remoteでデータベースへ接続します。
            ODatabaseDocumentTx db = new ODatabaseDocumentTx("remote:172.16.251.205/DocumentTest");
            db.open("root", "root");

            // TXTYPE.PESSIMISTIC Lockを指定
            db.begin(TXTYPE.PESSIMISTIC);

ただし、1.7.8の時点では未実装なため、以下の例外が投げられます。

java.lang.UnsupportedOperationException: Pessimistic transaction
    at com.orientechnologies.orient.core.db.record.ODatabaseRecordTx.begin(ODatabaseRecordTx.java:83)
    at com.orientechnologies.orient.core.db.record.ODatabaseRecordTx.begin(ODatabaseRecordTx.java:40)
    at com.orientechnologies.orient.core.db.ODatabaseRecordWrapperAbstract.begin(ODatabaseRecordWrapperAbstract.java:126)
    at transaction.Transaction_lock4wp2.main(Transaction_lock4wp2.java:22)