JDBC에서 삽입 ID를 얻는 방법은 무엇입니까?


385

INSERTJava에서 JDBC를 사용하여 데이터베이스 (내 경우에는 Microsoft SQL Server)의 레코드를 원합니다 . 동시에 삽입 ID를 얻고 싶습니다. JDBC API를 사용하여이를 달성하려면 어떻게해야합니까?


Query getGeneratedKeys () 에서 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.. if (generatedKeys.next()) primkey = generatedKeys.getInt(1); }
Yash

답변:


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을 얻는 것보다 insert 전에 시퀀스에서 다음 값을 얻는 것이 좋습니다. 후자는 멀티 스레드 환경 (예 : 웹 응용 프로그램 컨테이너)에서 잘못된 값을 반환 할 수 있기 때문입니다. JTDS MSSQL 드라이버는 getGeneratedKeys를 지원합니다.
JeeBee

4
(일반적으로 Oracle을 사용하므로 JDBC 드라이버 기능에 대한 기대치가 매우 낮습니다.)
JeeBee

7
Statement.RETURN_GENERATED_KEYS 옵션을 설정하지 않으면 발생하는 흥미로운 부작용은 오류 메시지입니다. "결과를 얻기 전에 명령문을 실행해야합니다."
Chris Winters

7
generatedKeys.next()반환 trueDB를하는 경우는 생성 된 키를 반환했습니다. 이봐 ResultSet. 는 close()단지 리소스를합니다. 그렇지 않으면 장기적으로 DB가 부족하여 응용 프로그램이 중단됩니다. 닫기 작업을 수행하는 유틸리티 방법을 직접 작성해야합니다. thisthis answer 도 참조하십시오 .
BalusC

5
대부분의 데이터베이스 / 드라이버에 대한 정답. 그러나 Oracle의 경우에는 작동하지 않습니다. Oracle의 경우 다음으로 변경하십시오. connection.prepareStatement (sql, new String [] { "PK column name"});
대럴 티그

24
  1. 생성 된 열 생성

    String generatedColumns[] = { "ID" };
  2. 이 생성 된 열을 진술에 전달하십시오.

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
  3. ResultSetStatement를 사용 하여 GeneratedKeys를 페치하기 위해 오브젝트 사용

    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 "마지막 ID"옵션을 훌륭하게 분리합니다 . -sql-server / -다른 두 개는 아직 필요하지 않았습니다.


4
응용 프로그램에 스레드가 하나만 있으면 경쟁 조건이 불가능하지 않습니다. 두 클라이언트가 행을 삽입하고 메소드로 ID를 검색하면 실패 할 수 있습니다.
11684

왜 네가? 여러 스레드를 허용 할 때 코드를 디버깅 해야하는 가난한 잔디가 아니라는 것이 기쁩니다!
mjaggard

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] 인 경우 하나 이상의 '?'를 포함 할 수있는 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 «다음 과 같은 columnName을 나열합니다 '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

다음 자바 코드를 사용하여 새로운 삽입 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를 가져올 수 있습니다 (일반적으로 정확히 하나의 항목을 포함 함).


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


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

실례지만이게 뭐야?
MoolsBytheway

1. createStatement방법은 Connection매개 변수를 기대하지 않습니다. 2.의 execute메소드 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.