如何在JDBC中获取插入ID?


385

我想INSERT在Java中使用JDBC在数据库(本例中为Microsoft SQL Server)中进行记录。同时,我想获取插入ID。如何使用JDBC API实现此目的?


在查询getGeneratedKeys()中保留自动生成的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 2015年

Answers:


650

如果它是自动生成的密钥,那么您可以使用Statement#getGeneratedKeys()它。您需要Statement使用与用于相同的名称进行调用INSERT。您首先需要使用创建语句Statement.RETURN_GENERATED_KEYS用于通知JDBC驱动程序以返回键。

这是一个基本示例:

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之后调用with RETURNING子句或SELECT CURRVAL(sequencename)(或执行此操作的任何特定于DB的特定语法)INSERT以获取最后生成的密钥。另请参阅此答案


4
最好在插入之前获取序列中的下一个值,而不要在插入之后获取当前值,因为后者可能在多线程环境(例如,任何Web应用程序容器)中返回错误的值。JTDS MSSQL驱动程序支持getGeneratedKeys。
JeeBee

4
(应该澄清一下,我通常使用Oracle,因此对JDBC驱动程序功能的期望通常非常低)。
JeeBee

7
错误消息中未设置Statement.RETURN_GENERATED_KEYS选项的一个有趣的副作用是错误消息,该消息完全晦涩难懂,“必须先执行该语句,然后才能获得任何结果。”
克里斯·温特斯

7
generatedKeys.next()返回true如果DB返回生成的密钥。看,这是一个ResultSet。的close()仅仅是免费的资源。否则,您的数据库将长期耗尽它们,并且您的应用程序将损坏。您只需要自己编写一些实用程序方法即可完成关闭任务。又见这个这个答案。
BalusC

5
对于大多数数据库/驱动程序,正确答案。对于Oracle,这不起作用。对于Oracle,请更改为:connection.prepareStatement(sql,new String [] {“ PK列名称”});
Darrell Teague 2014年

24
  1. 创建生成的列

    String generatedColumns[] = { "ID" };
  2. 将此生成的列传递给您的声明

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
  3. 使用ResultSet对象在语句上获取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,并撤回了最后一个ID,而不使用RETURN_GENERATED_KEYS属性或任何PreparedStatement。看起来像这样:

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“ last ID”选项:http : //msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the -sql-server / -不需要其他两个。


4
应用程序只有一个线程不会使竞争条件变为不可能:如果两个客户端插入一行并使用您的方法检索ID,则它可能会失败。
3684年

你怎么会 我只是很高兴我不是穷人,允许多线程时不必调试您的代码!
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代码获取新插入的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

除了发表评论,我只想回答帖子。


接口java.sql.PreparedStatement

  1. columnIndexes «您可以使用prepareStatement函数,该函数接受columnIndexes和SQL语句。 如果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 « 列出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();
    }
}

0

使用Hibernate的NativeQuery时,您需要返回ResultList而不是SingleResult,因为Hibernate会修改本机查询

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

也可以将其与normal一起使用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. executefrom 的方法Statement期望带查询的字符串。3.该execute方法返回:true如果第一个结果是ResultSet对象,则返回;否则返回0 。false如果是更新计数或没有结果。docs.oracle.com/javase/7/docs/api/java/sql/…–
atilacamurca
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.