JDBC PreparedStatement始终返回1作为自动生成的密钥

Cli*_*ote 6 java mysql jdbc

我有这个代码试图在数据库中插入一条记录:

try {
 Connection conn = getConnection();

 String sql = 
   "INSERT INTO myTable(userId,content,timestamp) VALUES(?,?,NOW())";
 PreparedStatement st = 
    conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);

 st.setLong(1, userId);
 st.setString(2, content);
 id = st.executeUpdate(); //this is the problem line            
} catch(Exception e) {}
Run Code Online (Sandbox Code Playgroud)

问题是,虽然正确插入了记录,但我想要id包含刚刚插入的记录的主键+ auto_increment id.但是,由于某种原因,它总是返回'1'作为id,可能是因为userId插入期间值为1.

我的桌子是InnoDB.起初userId是另一个表的外键,不管我已经删除了外键,甚至是userId列上的索引,但我仍然得到1作为返回值.

我有什么想法我做错了吗?

Bri*_*ach 8

PreparedStatment.executeUpdate()

返回:
(1)SQL数据操作语言(DML)语句的行计数或(2)0表示不返回任何内容的SQL语句

您需要使用execute()代替,并获得ResultSetgetGeneratedKeys(); 它将包含您想要的数据.

编辑补充:我看了你的问题,因为在该表的自动递增字段是不是 userId


Bas*_*que 6

接受的答案布赖恩·罗奇是正确的.我正在添加一些想法和完整代码的示例.

RETURN_GENERATED_KEYS难道平均"返回生成的密钥"

通过旗帜的措辞,原来的海报似乎很容易混淆,这是可以理解的Statement.RETURN_GENERATED_KEYS.与直觉相反,通过这个标志并没有改变的行为PreparedStatement::executeUpdate方式.该方法始终返回一个int受执行的SQL影响的行数."executeUpdate"方法永远不会返回生成的键.

int countRowsAffected = pstmt.executeUpdate();  // Always return number of rows affected, *not* the generated keys.
Run Code Online (Sandbox Code Playgroud)

问,并且你会收到

如果需要生成的密钥,则必须执行以下两个步骤:

  1. 传递旗帜,
  2. 请求ResultSet由仅包含生成的键值的行组成.

这种安排允许您添加获取生成的键的行为,同时保持其他所需的行为,计算受影响的行数.

示例代码

以下是从Java 8应用程序中获取的几乎真实的示例,该应用程序从数据馈送中删除数据.我认为在这种情况下,一个完整的例子可能比最小的例子更有用.

次要细节......这段代码可能不完美,语法或其他方面,因为我复制粘贴修改的真实源代码.我使用UUID数据类型而不是整数作为我表的代理主键.这些类CharHelperDBHelper是我自己的,其中的细节在这里并不重要.在xy变量是我自己的应用程序的有意义的数据的替代品.我的日志记录调用是针对SLF4J框架的.该UUID 十六进制字符串是链接在日志报告回原始源代码的便捷方式.数据库是Postgres,但是这种代码应该适用于支持生成密钥报告的任何数据库.

public UUID dbWrite (  String x , String y , DateTime whenRetrievedArg ) {
    if ( whenRetrievedArg == null ) {
        logger.error( "Passed null for whenRetrievedArg. Message # 2112ed1a-4612-4d5d-8cc5-bf27087a350d." );
        return null;
    }

    Boolean rowInsertComplete = Boolean.FALSE; // Might be used for debugging or logging or some logic in other copy-pasted methods.

    String method = "Method 'dbWrite'";
    String message = "Insert row for some_table_ in " + method + ". Message # edbea872-d3ed-489c-94e8-106a8e3b58f7.";
    this.logger.trace( message );

    String tableName = "some_table_";

    java.sql.Timestamp tsWhenRetrieved = new java.sql.Timestamp( whenRetrievedArg.getMillis() );  // Convert Joda-Time DatTime object to a java.sql.Timestamp object.

    UUID uuidNew = null;

    StringBuilder sql = new StringBuilder( AbstractPersister.INITIAL_CAPACITY_OF_SQL_STRING ); // private final static Integer INITIAL_CAPACITY_OF_SQL_STRING = 1024;
    sql.append( "INSERT INTO " ).append( tableName ).append( CharHelper.CHAR.PAREN_OPEN_SPACED ).append( " x_ , y_ " ).append( CharHelper.CHAR.PAREN_CLOSED ).append( DBHelper.SQL_NEWLINE );
    sql.append( "VALUES ( ? , ? , ?  ) " ).append( DBHelper.SQL_NEWLINE );
    sql.append( ";" );

    try ( Connection conn = DBHelper.instance().dataSource().getConnection() ;
Run Code Online (Sandbox Code Playgroud)

在这里,我们执行步骤#1,传递RETURN_GENERATED_KEYS旗帜.

            PreparedStatement pstmt = conn.prepareStatement( sql.toString() , Statement.RETURN_GENERATED_KEYS ); ) {
Run Code Online (Sandbox Code Playgroud)

我们继续准备并执行该声明.请注意,int countRows = pstmt.executeUpdate();返回受影响的行的计数,而不是生成的键.

        pstmt.setString( 1 , x ); 
        pstmt.setString( 2 , y ); 
        pstmt.setTimestamp( 3 , tsWhenRetrieved );  
        // Execute
        int countRows = pstmt.executeUpdate();  // Always returns an int, a count of affected rows. Does *not* return the generated keys.
        if ( countRows == 0 ) {  // Bad.
            this.logger.error( "Insert into database for new " + tableName + " failed to affect any rows. Message # 67e8de7e-67a5-42a6-a4fc-06929211e6e3." );
        } else if ( countRows == 1 ) {  // Good.
            rowInsertComplete = Boolean.TRUE;
        } else if ( countRows > 1 ) {  // Bad.
            rowInsertComplete = Boolean.TRUE;
            this.logger.error( "Insert into database for new " + tableName + " failed, affecting more than one row. Should not be possible. Message # a366e215-6cf2-4e5c-8443-0b5d537cbd68." );
        } else { // Impossible.
            this.logger.error( "Should never reach this Case-Else with countRows value " + countRows + " Message # 48af80d4-6f50-4c52-8ea8-98856873f3bb." );
        }
Run Code Online (Sandbox Code Playgroud)

这里我们执行步骤#2,请求生成密钥的ResultSet.在这个例子中,我们插入了一行并期望返回一个生成的密钥.

        if ( rowInsertComplete ) {
            // Return new row’s primary key value.
            ResultSet genKeys = pstmt.getGeneratedKeys();
            if ( genKeys.next() ) {
                uuidNew = ( UUID ) genKeys.getObject( 1 );  // ResultSet should have exactly one column, the primary key of INSERT table.
            } else {
                logger.error( "Failed to get a generated key returned from database INSERT. Message # 6426843e-30b6-4237-b110-ec93faf7537d." );
            }
        }
Run Code Online (Sandbox Code Playgroud)

其余的是错误处理和清理.请注意,我们在此代码的底部返回UUID,即生成的插入记录的主键.

    } catch ( SQLException ex ) {
        // We expect to have occasional violations of unique constraint on this table in this data-scraping app.
        String sqlState = ex.getSQLState();
        if ( sqlState.equals( DBHelper.SQL_STATE.POSTGRES.UNIQUE_CONSTRAINT_VIOLATION ) ) {  // SqlState code '23505' = 'unique_violation'.
            this.logger.trace( "Found existing row when inserting a '" + tableName + "' row for y: " + y + ". Expected to happen on most attempts. Message # 0131e8aa-0bf6-4d19-b1b3-2ed9d333df27." );
            return null; // Bail out.
        } else { // Else any other exception, throw it.
            this.logger.error( "SQLException during: " + method + " for table: " + tableName + ", for y: " + y + ". Message # 67908d00-2a5f-4e4e-815c-5e5a480d614b.\n" + ex );
            return null; // Bail out.
        }
    } catch ( Exception ex ) {
        this.logger.error( "Exception during: " + method + " for table: " + tableName + ", for y: " + y + ". Message # eecc25d8-de38-458a-bb46-bd6f33117969.\n" + ex );
        return null;  // Bail out.
    }

    if ( uuidNew == null ) {
        logger.error( "Returning a null uuidNew var. SQL: {} \nMessage # 92e2374b-8095-4557-a4ed-291652c210ae." , sql );
    }
    return uuidNew;
}
Run Code Online (Sandbox Code Playgroud)