7

私は JDBC と HSQLDB 2.2.9 を使用しています。id新しい行を DB に挿入し、その後、その(自動インクリメントに設定された PK) 値を保持する最も効率的で正確な方法は何ですか? これを行う必要がある理由はおそらくかなり明白ですが、議論のために例を挙げて説明します。

テーブルの行を参照する FK 制約を持つフィールドを持つCustomerテーブルがあるとします。新しい を作成したいのですが、これを行うには、まず新しい を作成し、新しい値を使用してを設定する必要があります。PersonIdPersonCustomerPersonPerson.idCustomer.PersonId

これにアプローチする 4 つの方法を見てきました。

  1. フィールドを にPerson設定して行を挿入します。HSQLDB は次の値を自動的に生成します。次に、テーブルに対してクエリを実行して、作成したばかりの値を取得し、それを使用して新しい行を作成します。idnullidPersonidCustomer

    これは、単一の整数値を取得するだけでもコストがかかるようです。

  2. テーブル内の次のid値を取得し、それをステートメントで使用して値を手動で設定します。同じ値を使用して設定します。その後の DB からの読み取りは必要ありません。PersonINSERTPerson.ididCustomer.PersonId

    id値が取得されると矛盾が生じる可能性がありますが、ステートメントが実行されるINSERT前に別の接続がテーブルで実行されます。INSERT INTO Person...

  3. INSERT上記のオプション 1 のように、ステートメントを実行し、id=null自動生成を許可するように設定します。次に、getGeneratedKeysメソッドを使用して、最後のステートメントで生成されたキーを取得します。

    これは良いオプションのように思えましたが、機能させることができませんでした。これが私のコードのスニペットです:

    // PreparedStatement prepared previously...
    preparedStatement.executeUpdate();
    ResultSet genKeys = preparedStatement.getGeneratedKeys();
    int id;
    if (genKeys.next()) {
        id = genKeys.getInt(1);
    }
    // Finish up method...
    

    このコードは に対して空ResultSetの値を返していましたgenKeys。メソッドの使い方がgetGeneratedKeys間違っていますか?これを機能させることができれば、これが進むべき道かもしれません。

  4. 再度、INSERT自動生成を許可するステートメントを実行しidます。次に、すぐに実行CALL IDENTITY()して、接続によって生成された最後のid値を取得します(ここで説明され、このSOの質問で言及されています)。

    これも妥当なオプションのようですが、追加のexecuteQuery. 良い面としては、実際に次のコードで動作させることができました。

    // INSERT statement executed here...
    statement = connection.createStatement();
    ResultSet rs = statement.executeQuery("CALL IDENTITY();");
    int id;
    if (rs.next()) id = rs.getInt(1);
    // Finish up method...
    

つまり、要約すると、最初の 2 つのオプションについては、あまり好きではありません。2 番目の 2 つは問題ないように見えますが、オプション 4 しか機能しませんでした。どのオプションが優先され、その理由は? オプション 3 が最適な場合、何が間違っているのでしょうか? また、私が言及していないより良い方法はありますか?「より良い」などの言葉が主観的である可能性があることは知っていますが、私は単純な DB を使用しており、DB を不整合の可能性に開かず、トランザクションの失敗率を増加させない最も直接的なソリューションが必要です (id既に存在するでレコードを作成します)。

これは基本的な (そして不可欠な) 質問のように思えますが、最善の方法についてのガイダンスはあまり見つかりませんでした。ありがとう。


編集:オプション3について説明するこの質問を見つけました。受け入れられた回答によると、Statement.RETURN_GENERATED_KEYSその機能を有効にするために必要なパラメーターを省略していたようです。コード スニペットではメソッドを示しませんでしたprepareStatementが、単一パラメーター バージョンを使用していました。オーバーロードされた 2 パラメーター バージョンを使用して再試行する必要があります。

私の質問に密接に関連している他のいくつかのSOの質問もあります。したがって、私の質問は重複していると見なされる可能性があると思います(以前に他の質問を見逃した方法はわかりません)。しかし、ある解決策が他の解決策よりも優れていると見なされるかどうかについてのガイダンスが欲しい. 今のところ、オプション 3 が機能する場合は、おそらくそれを使用します。

4

2 に答える 2

8

ネイザンの答えにコメントするほどの評判はありませんが、同じ問題を解決した方法は次のとおりです。

  • 列は ID 列のように見えましたが、IDENTITY として定義されていませんでした。
  • 上記のように、RETURN_GENERATED_KEYS を指定する必要があります。
  • 2 つの INSERT を順番に実行すると、2 番目の INSERT は生成されたキーを返さないようです。代わりに「CALL IDENTITY()」を使用してください。

HSQLDB 2.2.9 を使用した例:

CREATE TABLE MY_TABLE (
 ID INTEGER IDENTITY,
 NAME VARCHAR(30)
)

次にJavaで:

PreparedStatement result = cnx.prepareStatement(
    "INSERT INTO MY_TABLE(ID, NAME) VALUES(NULL, 'TOM');",
    RETURN_GENERATED_KEYS);
int updated = result.executeUpdate();
if (updated == 1) {
    ResultSet generatedKeys = result.getGeneratedKeys();
    if (generatedKeys.next()) {
        int key = generatedKeys.getInt(1);
    }
}
于 2013-09-09T09:05:40.797 に答える
1

ここではあまり行動を起こさないので、この質問に終止符を打つために答えていきましょう。さまざまなオプションを試した後、この質問を見た後、オプション 3 を機能させることができました。質問の編集で述べたように、オプション 3 を使用します。オプション 4 も問題なく機能しましたが、リンクされた質問に対する受け入れられた回答は信頼できる情報源から提供されているため、それに固執しています。これを始める前にその質問/回答を見ておけばよかったと思います。時間を節約できたでしょう!

于 2013-07-25T16:44:49.873 に答える