(轉)MYSQL獲取自增主鍵【4種方法】


  • 通過JDBC2.0提供的insertRow()方式
  • 通過JDBC3.0提供的getGeneratedKeys()方式
  • 通過SQL select LAST_INSERT_ID()函數
  • 通過SQL @@IDENTITY 變量

 

1. 通過JDBC2.0提供的insertRow()方式

自jdbc2.0以來,可以通過下面的方式執行。

 

  1. Statement stmt = null;  
  2. ResultSet rs = null;  
  3. try {  
  4.     stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,  // 創建Statement  
  5.                                 java.sql.ResultSet.CONCUR_UPDATABLE);  
  6.     stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");  
  7.     stmt.executeUpdate(                                                // 創建demo表  
  8.             "CREATE TABLE autoIncTutorial ("  
  9.             + "priKey INT NOT NULL AUTO_INCREMENT, "  
  10.             + "dataField VARCHAR(64), PRIMARY KEY (priKey))");  
  11.     rs = stmt.executeQuery("SELECT priKey, dataField "                 // 檢索數據  
  12.        + "FROM autoIncTutorial");  
  13.     rs.moveToInsertRow();                                              // 移動游標到待插入行(未創建的偽記錄)  
  14.     rs.updateString("dataField", "AUTO INCREMENT here?");              // 修改內容  
  15.     rs.insertRow();                                                    // 插入記錄  
  16.     rs.last();                                                         // 移動游標到最后一行  
  17.     int autoIncKeyFromRS = rs.getInt("priKey");                        // 獲取剛插入記錄的主鍵preKey  
  18.     rs.close();  
  19.     rs = null;  
  20.     System.out.println("Key returned for inserted row: "  
  21.         + autoIncKeyFromRS);  
  22. }  finally {  
  23.     // rs,stmt的close()清理  
  24. }  
Statement stmt = null;
ResultSet rs = null;
try {
    stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,  // 創建Statement
                                java.sql.ResultSet.CONCUR_UPDATABLE);
    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
    stmt.executeUpdate(                                                // 創建demo表
            "CREATE TABLE autoIncTutorial ("
            + "priKey INT NOT NULL AUTO_INCREMENT, "
            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
    rs = stmt.executeQuery("SELECT priKey, dataField "                 // 檢索數據
       + "FROM autoIncTutorial");
    rs.moveToInsertRow();                                              // 移動游標到待插入行(未創建的偽記錄)
    rs.updateString("dataField", "AUTO INCREMENT here?");              // 修改內容
    rs.insertRow();                                                    // 插入記錄
    rs.last();                                                         // 移動游標到最后一行
    int autoIncKeyFromRS = rs.getInt("priKey");                        // 獲取剛插入記錄的主鍵preKey
    rs.close();
    rs = null;
    System.out.println("Key returned for inserted row: "
        + autoIncKeyFromRS);
}  finally {
    // rs,stmt的close()清理
}

優點:早期較為通用的做法

 

缺點:需要操作ResultSet的游標,代碼冗長。

2. 通過JDBC3.0提供的getGeneratedKeys()方式

  1. Statement stmt = null;  
  2. ResultSet rs = null;  
  3. try {  
  4.     stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,  
  5.                                 java.sql.ResultSet.CONCUR_UPDATABLE);    
  6.     // ...  
  7.     // 省略若干行(如上例般創建demo表)  
  8.     // ...  
  9.     stmt.executeUpdate(  
  10.             "INSERT INTO autoIncTutorial (dataField) "  
  11.             + "values ('Can I Get the Auto Increment Field?')",  
  12.             Statement.RETURN_GENERATED_KEYS);                      // 向驅動指明需要自動獲取generatedKeys!  
  13.     int autoIncKeyFromApi = -1;  
  14.     rs = stmt.getGeneratedKeys();                                  // 獲取自增主鍵!  
  15.     if (rs.next()) {  
  16.         autoIncKeyFromApi = rs.getInt(1);  
  17.     }  else {  
  18.         // throw an exception from here  
  19.     }   
  20.     rs.close();  
  21.     rs = null;  
  22.     System.out.println("Key returned from getGeneratedKeys():"  
  23.         + autoIncKeyFromApi);  
  24. }  finally { ... }  
Statement stmt = null;
ResultSet rs = null;
try {
    stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
                                java.sql.ResultSet.CONCUR_UPDATABLE);  
    // ...
    // 省略若干行(如上例般創建demo表)
    // ...
    stmt.executeUpdate(
            "INSERT INTO autoIncTutorial (dataField) "
            + "values ('Can I Get the Auto Increment Field?')",
            Statement.RETURN_GENERATED_KEYS);                      // 向驅動指明需要自動獲取generatedKeys!
    int autoIncKeyFromApi = -1;
    rs = stmt.getGeneratedKeys();                                  // 獲取自增主鍵!
    if (rs.next()) {
        autoIncKeyFromApi = rs.getInt(1);
    }  else {
        // throw an exception from here
    } 
    rs.close();
    rs = null;
    System.out.println("Key returned from getGeneratedKeys():"
        + autoIncKeyFromApi);
}  finally { ... }
這種方式只需要2個步驟:1. 在executeUpdate時激活自動獲取key; 2.調用Statement的getGeneratedKeys()接口 優點:
1. 操作方便,代碼簡潔
2. jdbc3.0的標准
3. 效率高,因為沒有額外訪問數據庫
 
這里補充下,
a.在jdbc3.0之前,每個jdbc driver的實現都有自己獲取自增主鍵的接口。在mysql jdbc2.0的driver  org.gjt.mm.mysql中,getGeneratedKeys()函數就實現在org.gjt.mm.mysql.jdbc2.Staement.getGeneratedKeys()中。這樣直接引用的話,移植性會有很大影響。JDBC3.0通過標准的getGeneratedKeys很好的彌補了這點。
b.關於getGeneratedKeys(),官網還有更詳細解釋:OracleJdbcGuide
 

3. 通過SQL select LAST_INSERT_ID()

  1. Statement stmt = null;  
  2. ResultSet rs = null;  
  3. try {  
  4.     stmt = conn.createStatement();  
  5.     // ...  
  6.     // 省略建表  
  7.     // ...  
  8.     stmt.executeUpdate(  
  9.             "INSERT INTO autoIncTutorial (dataField) "  
  10.             + "values ('Can I Get the Auto Increment Field?')");  
  11.     int autoIncKeyFromFunc = -1;  
  12.     rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");             // 通過額外查詢獲取generatedKey  
  13.     if (rs.next()) {  
  14.         autoIncKeyFromFunc = rs.getInt(1);  
  15.     }  else {  
  16.         // throw an exception from here  
  17.     }   
  18.     rs.close();  
  19.     System.out.println("Key returned from " +  
  20.                        "'SELECT LAST_INSERT_ID()': " +  
  21.                        autoIncKeyFromFunc);  
  22. }  finally {...}  
Statement stmt = null;
ResultSet rs = null;
try {
    stmt = conn.createStatement();
    // ...
    // 省略建表
    // ...
    stmt.executeUpdate(
            "INSERT INTO autoIncTutorial (dataField) "
            + "values ('Can I Get the Auto Increment Field?')");
    int autoIncKeyFromFunc = -1;
    rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");             // 通過額外查詢獲取generatedKey
    if (rs.next()) {
        autoIncKeyFromFunc = rs.getInt(1);
    }  else {
        // throw an exception from here
    } 
    rs.close();
    System.out.println("Key returned from " +
                       "'SELECT LAST_INSERT_ID()': " +
                       autoIncKeyFromFunc);
}  finally {...}
這種方式沒什么好說的,就是額外查詢一次函數LAST_INSERT_ID().
優點:簡單方便
缺點:相對JDBC3.0的getGeneratedKeys(),需要額外多一次數據庫查詢。
 
補充:
1. 這個函數,在mysql5.5手冊的定義是:“returns a BIGINT (64-bit) value representing the first automatically generated value successfully inserted for an AUTO_INCREMENT column as a result of the most recently executed INSERT statement.”。文檔點此
2. 這個函數,在connection維度上是“線程安全的”。就是說,每個mysql連接會有個獨立保存LAST_INSERT_ID()的結果,並且只會被當前連接最近一次insert操作所更新。也就是2個連接同時執行insert語句時候,分別調用的LAST_INSERT_ID()不會相互覆蓋。舉個栗子:連接A插入表后LAST_INSERT_ID()返回100,連接B插入表后LAST_INSERT_ID()返回101,但是連接A重復執行LAST_INSERT_ID()的時候,始終返回100,而不是101。這個可以通過監控mysql連接數和執行結果來驗證,這里不詳述實驗過程。
3.  在上面那點的基礎上,如果在同一個連接的前提下同時執行insert,那可能2次操作的返回值會相互覆蓋。因為LAST_INSERT_ID()的隔離程度是連接級別的。這點,getGeneratedKeys()是可以做的更好,因為getGeneratedKeys()是statement級別的。同個connection的多次statement,getGeneratedKeys()是不會被相互覆蓋。
 

4. 通過SQL SELECT @@IDENTITY

這個方式和LAST_INSERT_ID()效果是一樣的。官網文檔如此表述:“This variable is a synonym for the last_insert_id variable. It exists for compatibility with other database systems. You can read its value with SELECT @@identity, and set it using SET identity.” 文檔點此
 
重要補充:
無論是SELECT LAST_INSERT_ID()還是SELECT @@IDENTITY,對於一條insert語句插入多條記錄,永遠只會返回第一條插入記錄的generatedKey.如:
  1. INSERT INTO t VALUES  
  2.     -> (NULL, 'Mary'), (NULL, 'Jane'), (NULL, 'Lisa');  
INSERT INTO t VALUES
    -> (NULL, 'Mary'), (NULL, 'Jane'), (NULL, 'Lisa');
LAST_INSERT_ID(), @@IDENTITY都只會返回'Mary'所在的那條記錄的generatedKey

小結

所以,最好還是通過JDBC3 提供的getGeneratedKeys()函數來獲取insert記錄的主鍵。不但簡單,而且效率高。
 
在mybatis中,就有相關設置:
  1. <insert id="save" parameterType="MappedObject" useGeneratedKeys="true" keyProperty="id">  
  2. </insert>  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM