3

トランザクションの分離レベルを使用してさまざまな同時実行の問題に対処する方法を理解するために、いくつかのテストを行おうとしています。から始めましTRANSACTION_READ_COMMITEDたが、最も単純なシナリオでは期待どおりに動作しません。コードは次のとおりです。

try(Connection connection1 = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
    connection1.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
    connection1.setAutoCommit(false);

    try(Connection connection2 = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
        connection2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
        connection2.setAutoCommit(false);

        assertEquals(0, selectAll(connection1));
        assertEquals(0, selectAll(connection2));

        insertOne(connection1);
        assertEquals(0, selectAll(connection2)); // there is 1 row!
    }
}

ここでは、2 つの同時接続をセットアップし、両方でトランザクションを開始し、最初の接続で変更を行い、2 番目の接続ではそれらが表示されないことを期待しています。これは機能しません。接続 1 で行われたコミットされていない変更は、接続 2 に表示されます。

インメモリデータベースで組み込みモードで実行されているHSQLDB 2.3.2を使用しています。私の selectAll/insert ヘルパー メソッドの実装は次のとおりです。

private static void initSchema() throws SQLException {
    try(Connection connection = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
        try (PreparedStatement s = connection.prepareStatement(
                "create table Notes(text varchar(256) not null)")) {
            s.executeUpdate();
        }
    }
}

private static int selectAll(Connection connection) throws SQLException {
    int count = 0;
    try (PreparedStatement s = connection.prepareStatement("select * from Notes")) {
        s.setQueryTimeout(1);
        try (ResultSet resultSet = s.executeQuery()) {
            while (resultSet.next()) {
                ++count;
            }
        }
    }

    return count;
}

private static void insertOne(Connection connection) throws SQLException {
    try(PreparedStatement s = connection.prepareStatement("insert into Notes(text) values(?)")) {
        s.setString(1, "hello");
        s.setQueryTimeout(1);
        s.executeUpdate();
    }
}

完全なテストはここにあります: https://gist.github.com/loki2302/aad49a5a2c26d5fda2b3

このコードに問題がありますか、それとも HSQLDB が本来の動作をしていませんか?

更新: wiki を読み直した後、ここでの私の考えは間違っていると思います。ここに表示されるのは「ファントム リード」です。READ_COMMITTEDファントム読み取りが発生しないことを保証するものではありません。代わりに確認する必要があるのは、テーブルを 1 行で事前に生成し、更新して、変更がコミットされない限り、connection1この変更が表示されないようにすることです。connection2さらに、この変更がコミットの直後に見えるようになることは一般的に保証されていません。見えるようになるかもしれませんが、保証されていません。

4

1 に答える 1

1

インプロセス データベースのセットアップは、実行中のテストには適していません。

代替手段は次のとおりです。

  1. サーバーを実行してみて、MVCC でテストを繰り返します。
  2. MVCC およびインプロセス データベースとの接続ごとに個別のスレッドを使用してみてください。

例のようにインプロセスで使用する場合、HSQLDB では、各接続を個別のスレッドが所有する必要があります。そうしないと、Java 同時実行機能が機能しません。

個別のスレッドで実行する場合、これを機能させるには MVCC トランザクション モデルを使用する必要があります。使用したデフォルトのロックモードでは、挿入はコミットされるまで他の接続をロックアウトします。

于 2015-01-31T23:09:33.710 に答える