JDBCで挿入IDを取得するにはどうすればよいですか?


385

INSERTJavaでJDBCを使用してデータベース(私の場合はMicrosoft SQL Server)に記録したい。同時に、挿入IDを取得したいと思います。JDBC APIを使用してこれをどのように達成できますか?


AutoGenreratedであるIDをクエリString sql = "INSERT INTO 'yash'.'mytable' ('name') VALUES (?)"; int primkey = 0 ; PreparedStatement pstmt = con.prepareStatement(sql, new String[] { "id" }/*Statement.RETURN_GENERATED_KEYS*/); pstmt.setString(1, name); if (pstmt.executeUpdate() > 0) { java.sql.ResultSet generatedKeys = pstmt.getGeneratedKeys();に残します。 if (generatedKeys.next()) primkey = generatedKeys.getInt(1); }
Yash、2015年

回答:


650

自動生成されたキーの場合Statement#getGeneratedKeys()、これに使用できます。Statementに使用されているものと同じものを呼び出す必要がありますINSERT。まず、JDBCドライバーにキーを返すよう通知するために使用するステートメントを作成する必要がありStatement.RETURN_GENERATED_KEYSます。

基本的な例は次のとおりです。

public void create(User user) throws SQLException {
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL_INSERT,
                                      Statement.RETURN_GENERATED_KEYS);
    ) {
        statement.setString(1, user.getName());
        statement.setString(2, user.getPassword());
        statement.setString(3, user.getEmail());
        // ...

        int affectedRows = statement.executeUpdate();

        if (affectedRows == 0) {
            throw new SQLException("Creating user failed, no rows affected.");
        }

        try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
            if (generatedKeys.next()) {
                user.setId(generatedKeys.getLong(1));
            }
            else {
                throw new SQLException("Creating user failed, no ID obtained.");
            }
        }
    }
}

動作するかどうかは、JDBCドライバーに依存していることに注意してください。現在、最新のバージョンのほとんどは動作しますが、私が正しければ、Oracle JDBCドライバーはまだこれでやや面倒です。MySQLとDB2は、これを古くからサポートしています。PostgreSQLはそれをサポートするようになりました。MSSQLを使用したことがないのでコメントできません。

Oracleの場合、CallableStatementwith RETURNING句またはSELECT CURRVAL(sequencename)(またはそれを行うためのDB固有の構文)をINSERT同じトランザクションの直後に呼び出して、最後に生成されたキーを取得できます。この回答も参照してください。


4
挿入後にcurrvalを取得するよりも、挿入の前にシーケンスの次の値を取得する方が適切です。後者はマルチスレッド環境(たとえば、任意のWebアプリコンテナー)で誤った値を返す可能性があるためです。JTDS MSSQLドライバーはgetGeneratedKeysをサポートします。
JeeBee、2009

4
(私は通常Oracleを使用するので、JDBCドライバーの機能に対する期待は通常非常に低いことを明確にする必要があります)
JeeBee 2009

7
Statement.RETURN_GENERATED_KEYSオプションを設定しないことの興味深い副作用は、エラーメッセージです。これは、「結果を取得する前にステートメントを実行する必要がある」という完全に不明瞭なメッセージです。
Chris Winters、

7
generatedKeys.next()リターンtrueDBが生成した鍵を返した場合。見て、それはResultSetです。close()ちょうど自由なリソースにあります。そうしないと、長期的にはDBが不足し、アプリケーションが破損します。終了タスクを実行するユーティリティメソッドを自分で作成する必要があります。これこの回答も参照してください。
BalusC 2011年

5
ほとんどのデータベース/ドライバーの正解。ただし、Oracleの場合は機能しません。Oracleの場合、次のように変更します。connection.prepareStatement(sql、new String [] {"PK column name"});
ダレルティーグ2014年

24
  1. 生成された列を作成

    String generatedColumns[] = { "ID" };
  2. この生成された列をステートメントに渡します

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
  3. ResultSetオブジェクトを使用してステートメントで生成されたキーをフェッチする

    ResultSet rs = stmtInsert.getGeneratedKeys();
    
    if (rs.next()) {
        long id = rs.getLong(1);
        System.out.println("Inserted ID -" + id); // display inserted record
    }

8

シングルスレッドのJDBCベースのアプリケーションからMicrosoft SQL Server 2008 R2にアクセスし、RETURN_GENERATED_KEYSプロパティやPreparedStatementを使用せずに最後のIDをプルバックしています。次のようになります。

private int insertQueryReturnInt(String SQLQy) {
    ResultSet generatedKeys = null;
    int generatedKey = -1;

    try {
        Statement statement = conn.createStatement();
        statement.execute(SQLQy);
    } catch (Exception e) {
        errorDescription = "Failed to insert SQL query: " + SQLQy + "( " + e.toString() + ")";
        return -1;
    }

    try {
        generatedKey = Integer.parseInt(readOneValue("SELECT @@IDENTITY"));
    } catch (Exception e) {
        errorDescription = "Failed to get ID of just-inserted SQL query: " + SQLQy + "( " + e.toString() + ")";
        return -1;
    }

    return generatedKey;
} 

このブログ投稿では、SQL Serverの3つの主要な「最後のID」オプションを適切に分離しています。http//msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the -sql-server / -他の2つはまだ必要ありません。


4
アプリケーションにスレッドが1つしかないことは、競合状態を不可能にしません。2つのクライアントが行を挿入してメソッドでIDを取得すると、失敗する可能性があります。
11684

なぜあなたは?複数のスレッドを許可するときにコードをデバッグする必要がある貧しいsodではないことを嬉しく思います!
mjaggard 2013

6

の使用中に「サポートされていない機能」エラーが発生した場合はStatement.RETURN_GENERATED_KEYS、次のことを試してください。

String[] returnId = { "BATCHID" };
String sql = "INSERT INTO BATCH (BATCHNAME) VALUES ('aaaaaaa')";
PreparedStatement statement = connection.prepareStatement(sql, returnId);
int affectedRows = statement.executeUpdate();

if (affectedRows == 0) {
    throw new SQLException("Creating user failed, no rows affected.");
}

try (ResultSet rs = statement.getGeneratedKeys()) {
    if (rs.next()) {
        System.out.println(rs.getInt(1));
    }
    rs.close();
}

BATCHID自動生成されたIDはどこにありますか。


もしかしてBATCHID
MoolsBytheway

素晴らしい答え!!!
Hasitha Jayawardana

3

SQLServer 2008 を使用していますが、開発上の制限があります。新しいドライバーを使用できません。「com.microsoft.jdbc.sqlserver.SQLServerDriver」を使用する必要があります(「com.microsoft.sqlserver.jdbcは使用できません」 .SQLServerDriver」)。

これが、ソリューションconn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)java.lang.AbstractMethodErrorスローした理由です。この状況で、私が見つけた可能な解決策は、Microsoftが提案した古い解決策です 。JDBCを使用して@@ IDENTITY値を取得する方法

import java.sql.*; 
import java.io.*; 

public class IdentitySample
{
    public static void main(String args[])
    {
        try
        {
            String URL = "jdbc:microsoft:sqlserver://yourServer:1433;databasename=pubs";
            String userName = "yourUser";
            String password = "yourPassword";

            System.out.println( "Trying to connect to: " + URL); 

            //Register JDBC Driver
            Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();

            //Connect to SQL Server
            Connection con = null;
            con = DriverManager.getConnection(URL,userName,password);
            System.out.println("Successfully connected to server"); 

            //Create statement and Execute using either a stored procecure or batch statement
            CallableStatement callstmt = null;

            callstmt = con.prepareCall("INSERT INTO myIdentTable (col2) VALUES (?);SELECT @@IDENTITY");
            callstmt.setString(1, "testInputBatch");
            System.out.println("Batch statement successfully executed"); 
            callstmt.execute();

            int iUpdCount = callstmt.getUpdateCount();
            boolean bMoreResults = true;
            ResultSet rs = null;
            int myIdentVal = -1; //to store the @@IDENTITY

            //While there are still more results or update counts
            //available, continue processing resultsets
            while (bMoreResults || iUpdCount!=-1)
            {           
                //NOTE: in order for output parameters to be available,
                //all resultsets must be processed

                rs = callstmt.getResultSet();                   

                //if rs is not null, we know we can get the results from the SELECT @@IDENTITY
                if (rs != null)
                {
                    rs.next();
                    myIdentVal = rs.getInt(1);
                }                   

                //Do something with the results here (not shown)

                //get the next resultset, if there is one
                //this call also implicitly closes the previously obtained ResultSet
                bMoreResults = callstmt.getMoreResults();
                iUpdCount = callstmt.getUpdateCount();
            }

            System.out.println( "@@IDENTITY is: " + myIdentVal);        

            //Close statement and connection 
            callstmt.close();
            con.close();
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

        try
        {
            System.out.println("Press any key to quit...");
            System.in.read();
        }
        catch (Exception e)
        {
        }
    }
}

この解決策は私にとってうまくいきました!

これが役に立てば幸いです!


1

コメントの代わりに、投稿に答えたいだけです。


インターフェースjava.sql.PreparedStatement

  1. columnIndexes «columnIndexesとSQLステートメントを受け入れるprepareStatement関数を使用できます。 columnIndexesで使用できる定数フラグがStatement.RETURN_GENERATED_KEYS 1またはStatement.NO_GENERATED_KEYS [2]の場合、1つ以上の「?」を含む可能性のあるSQLステートメント INパラメーターのプレースホルダー。

    構文«

    Connection.prepareStatement(String sql, int autoGeneratedKeys)
    Connection.prepareStatement(String sql, int[] columnIndexes)

    例:

    PreparedStatement pstmt = 
        conn.prepareStatement( insertSQL, Statement.RETURN_GENERATED_KEYS );

  1. columnNames «のようなcolumnNamesをリストし'id', 'uniqueID', ...ます。返される自動生成キーを含むターゲットテーブル。SQLステートメントがステートメントでない場合、ドライバーはそれらを無視しますINSERT

    構文«

    Connection.prepareStatement(String sql, String[] columnNames)

    例:

    String columnNames[] = new String[] { "id" };
    PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );

完全な例:

public static void insertAutoIncrement_SQL(String UserName, String Language, String Message) {
    String DB_URL = "jdbc:mysql://localhost:3306/test", DB_User = "root", DB_Password = "";

    String insertSQL = "INSERT INTO `unicodeinfo`( `UserName`, `Language`, `Message`) VALUES (?,?,?)";
            //"INSERT INTO `unicodeinfo`(`id`, `UserName`, `Language`, `Message`) VALUES (?,?,?,?)";
    int primkey = 0 ;
    try {
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        Connection conn = DriverManager.getConnection(DB_URL, DB_User, DB_Password);

        String columnNames[] = new String[] { "id" };

        PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );
        pstmt.setString(1, UserName );
        pstmt.setString(2, Language );
        pstmt.setString(3, Message );

        if (pstmt.executeUpdate() > 0) {
            // Retrieves any auto-generated keys created as a result of executing this Statement object
            java.sql.ResultSet generatedKeys = pstmt.getGeneratedKeys();
            if ( generatedKeys.next() ) {
                primkey = generatedKeys.getInt(1);
            }
        }
        System.out.println("Record updated with id = "+primkey);
    } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    }
}

このソリューションをマルチスレッドのランタイム環境で使用しても安全ですか?
のプロトタイプ

1

次のJavaコードを使用して、新しく挿入されたIDを取得できます。

ps = con.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, quizid);
ps.setInt(2, userid);
ps.executeUpdate();

ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
    lastInsertId = rs.getInt(1);
}

0

HibernateのNativeQueryでは、Hibernateがネイティブクエリを変更するため、SingleResultではなくResultListを返す必要があります。

INSERT INTO bla (a,b) VALUES (2,3) RETURNING id

お気に入り

INSERT INTO bla (a,b) VALUES (2,3) RETURNING id LIMIT 1

単一の結果を取得しようとすると、ほとんどのデータベース(少なくともPostgreSQL)で構文エラーがスローされます。その後、結果のIDをリストからフェッチできます(通常は1つのアイテムのみが含まれます)。


0

通常のとそれを使用することが可能であるStatement(だけでなく、同様のPreparedStatement

Statement statement = conn.createStatement();
int updateCount = statement.executeUpdate("insert into x...)", Statement.RETURN_GENERATED_KEYS);
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
  if (generatedKeys.next()) {
    return generatedKeys.getLong(1);
  }
  else {
    throw new SQLException("Creating failed, no ID obtained.");
  }
}

0

私の場合->

ConnectionClass objConnectionClass=new ConnectionClass();
con=objConnectionClass.getDataBaseConnection();
pstmtGetAdd=con.prepareStatement(SQL_INSERT_ADDRESS_QUERY,Statement.RETURN_GENERATED_KEYS);
pstmtGetAdd.setString(1, objRegisterVO.getAddress());
pstmtGetAdd.setInt(2, Integer.parseInt(objRegisterVO.getCityId()));
int addId=pstmtGetAdd.executeUpdate();              
if(addId>0)
{
    ResultSet rsVal=pstmtGetAdd.getGeneratedKeys();
    rsVal.next();
    addId=rsVal.getInt(1);
}

それでも、それを手に入れるには長いアプローチだと思います。もっと圧縮されたソリューションもあると思います。
TheSagya 2018


-6
Connection cn = DriverManager.getConnection("Host","user","pass");
Statement st = cn.createStatement("Ur Requet Sql");
int ret  = st.execute();

すみませんが、これはどうなっているのでしょうか。
MoolsBytheway 2018

1. createStatementからのメソッドConnectionは、パラメータを期待していません。2. executefromメソッドはStatement、クエリ付きの文字列を期待しています。3. executeメソッドは次を返します。true最初の結果がResultSetオブジェクトの場合。false更新カウントであるか、結果がない場合。docs.oracle.com/javase/7/docs/api/java/sql/...
atilacamurca
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.