2013-07-25 33 views
5

我正在使用JDBC和HSQLDB 2.2.9。什麼是最有效和準確的方式來插入一個新的行到數據庫中,並且隨後保留其id(PK設置爲自動增量)值?我需要做的原因可能是很明顯的,但我會討論一個例子說明:如何使用JDBC和HSQLDB檢索先前自動生成的PK ID值

說有,有一個PersonId場與FK約束從Person表指的是一排Customer表。我想創建一個新的Customer,但要這樣做,我需要先創建一個新的Person並使用新的Person.id值來設置Customer.PersonId

我見過四種方式來處理這個:

  1. 插入Personid字段設置爲null。 HSQLDB自動生成下一個id值。然後在Person表上執行查詢以獲得剛剛創建的id值,並使用它創建新的Customer行。

    這似乎很昂貴,只是檢索一個整數值。

  2. 獲取在Person表中的下id值,並用它在INSERT語句來手動設置Person.id值。使用相同的id值來設置Customer.PersonId。不需要從數據庫中進行後續讀取。

    如果獲得id值,但在我的INSERT INTO Person...語句執行之前另一個連接在表中執行INSERT,則可能會出現不一致。

  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... 
    

    此代碼是用於返回一個genKeysResultSet。我是否錯誤地使用getGeneratedKeys方法?如果我能夠實現這一目標,這可能是一條可行的路。

  4. 再次執行INSERT語句,允許自動生成id。然後立即執行CALL IDENTITY()以檢索連接生成的最後一個id值(如解釋here並在this 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... 
    

因此,簡言之,前兩個選項我沒瘋的。後兩個看起來不錯,但我只能得到選項4的工作。哪個選項是首選,爲什麼?如果選項3是最好的,我做錯了什麼?另外,有沒有更好的方法,我沒有提到?我知道「更好」這樣的詞可能是主觀的,但我正在使用一個簡單的數據庫,並希望最直接的解決方案不會打開數據庫以避免可能的不一致,也不會增加事務失敗率(由於嘗試創建一個已經存在的id的記錄)。

這似乎是一個基本的問題(而且是必不可少的),但我找不到有關最佳方法的很多指導。謝謝。


編輯: 我剛剛發現 this問題,討論我的選擇3.根據公認的答案,看來我要離開了,以使該功能所需要的 Statement.RETURN_GENERATED_KEYS參數。我沒有在我的代碼片段中顯示 prepareStatement方法,但我使用的是單參數版本。我需要使用重載的雙參數版本重試。

還有一些其他的問題與我的問題密切相關。所以,我猜我可能被認爲是重複的(不知道我以前錯過了其他問題)。但是我仍然喜歡關於一種解決方案是否比其他解決方案更好的指導。現在,如果我得到選項3的工作,我可能會去。

+0

說實話,我不明白爲什麼你不會致使堅持自己的解決方案1號。在新插入的行的ID上進行SELECT操作通常會產生一個查詢開銷,但同樣,所有其他選項也會這樣做。 –

+0

我不會在'id'上選擇'',而是在包含另一個唯一密鑰的其他字段上。 'id'值就是我要找的。但是,無論查詢的詳細信息如何,您對「查詢常見開銷」的評論仍然存在。但是,我不確定選項3中的開銷。我認爲這些信息將從前面的語句執行中遺留下來。對於選項4,我不確定執行「CALL IDENTITY()」查詢是否比「常規」SELECT語句更便宜。 – neizan

+0

我不知道它是否也是。就個人而言,我不會擔心插入後只有一個查詢,但自然我不知道你的任務的具體細節。 –

回答

1

這裏沒有太多的動作,所以我會繼續前進並回答以解決此問題。在玩過不同的選項後,在看到this的問題後,我能夠讓我的選項3工作。就像我在我的問題的編輯中提到的那樣,我將使用選項3.選項4也可以正常工作,但由於對於鏈接問題的接受答案是由有信譽的來源提供的,因此我堅持這一點。我希望在開始這個之前我已經看到了這個問題/答案,我已經節省了一些時間!

+3

選項3是最好的選擇,因爲在INSERT語句完成時返回生成的密鑰,從而最大限度地減少其他選項所需的額外操作。 – fredt

+0

這就是我的想法,也是我選擇這個選項的主要原因。感謝您的反饋。 – neizan

+0

如果您願意使用ORM,他們將爲您完成所有工作。請參閱[Sormula身份欄](http://www.sormula.org/identity/)示例。 –

7

我沒有足夠的聲譽上neizan的回答發表評論,但這裏是我如何解決同樣的問題:

  • 列看上去就像一個ID列,但它並沒有定義爲身份;
  • 如上所述,您需要指定RETURN_GENERATED_KEYS。
  • 它看起來像是你按順序執行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); 
    } 
}