我有这段代码试图在数据库中插入一条记录:
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) {}
问题是,虽然正确插入了记录,但我希望id
包含刚刚插入的记录的主键+ auto_increment id。但是,出于某种原因,它总是返回'1'作为id,可能是因为插入过程中userId
的值为1。
我的桌子是InnoDB。起初userId
是另一个表的外键,不管我已经删除了外键,甚至是userId列上的索引,但我仍然得到1作为返回值。
任何想法我做错了什么?
答案 0 :(得分:8)
<强> PreparedStatment.executeUpdate()强>
<强>返回:强>
(1)SQL数据操作语言(DML)语句的行计数或(2)0表示不返回任何内容的SQL语句
您需要使用execute()
代替ResultSet
获取getGeneratedKeys()
;它将包含您想要的数据。
编辑添加:我读了你的问题,因为表中有一个不 userId
答案 1 :(得分:6)
Brian Roach的accepted Answer是正确的。我正在添加一些想法和完整代码的示例。
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.
如果您想要生成密钥,则必须执行两个步骤:
ResultSet
由仅包含生成的键值的行组成。这种安排允许您添加获取生成的键的行为,同时保持其他所需的行为,计算受影响的行数。
这是一个几乎真实的例子,它来自Java 8应用程序,它从数据源中删除数据。我认为在这种情况下,一个完整的例子可能比最小的例子更有用。
次要细节......这段代码可能不完美,语法或其他方面,因为我复制粘贴修改的真实源代码。我使用UUID data type而不是整数作为我的表的surrogate主键。类CharHelper
和DBHelper
是我自己的,其详细信息在这里并不重要。 x
和y
变量是我自己应用的有意义数据的替代品。我的日志记录调用是SLF4J框架。 UUID hex字符串是将日志中的报表链接回原始源代码的便捷方式。数据库是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() ;
我们执行第1步,传递RETURN_GENERATED_KEYS
标记。
PreparedStatement pstmt = conn.prepareStatement( sql.toString() , Statement.RETURN_GENERATED_KEYS ); ) {
我们继续准备并执行该声明。请注意,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." );
}
这里我们执行步骤#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." );
}
}
其余的是错误处理和清理。请注意我们返回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;
}
答案 2 :(得分:2)
String SQLQuery=" ";
String generatedKeys[]= {"column_name"};//'column_name' auto-increment column
prepSt = Connection.prepareStatement(SQLQuery,generatedKeys);
prepSt.setInt(1, 1234);
.....
.....
....
prepSt.executeUpdate();
ResultSet rs = prepSt.getGeneratedKeys; // used same PreparedStatement object as used for Insert .
if(rs.next()) {
int id=rs.getLong("column_name");
System.out.println(id);
}
} catch (SQLException e) {
}
答案 3 :(得分:1)
如果您在数据库中设置了userId
自动增量,则不应尝试自行添加。您应该插入NULL
,它会自动为您增加! (线索在名称中!)
此外,您没有更新您的表,您正在插入它。所以你不要执行更新()。尝试...
PreparedStatement pst = conn.prepareStatement("INSERT INTO myTable(userId,content,timestamp) VALUES(NULL,?,NOW())");
pst.setString(1, content);
pst.executeQuery();
答案 4 :(得分:1)
你得到的是'Rows insetrted'的通知(对于INSERT语句)。我们使用此方法来了解我们的DML查询是否成功。以下是使用[prepareStatement(yourSQL,Statement.RETURN_GENERATED_KEYS)]获取自动生成ID的方法。请注意,此方法仅返回RowID引用。要获得实际的val,请参阅方法2。
(方法1)
Try{
String yourSQL="insert into Table1(Id,Col2,Col3) values(SEQ.nextval,?,?)";
myPrepStatement = <Connection>.prepareStatement(yourSQL, Statement.RETURN_GENERATED_KEYS);
myPrepStatement.setInt(1, 123);
myPrepStatement.setInt(2, 123);
myPrepStatement.executeUpdate();
ResultSet rs = getGeneratedKeys;
if(rs.next()) {
java.sql.RowId rid=rs.getRowId(1);
//what you get is only a RowId ref, try make use of it anyway U could think of
System.out.println(rid);
}
} catch (SQLException e) {
}
(方法2)
Try{
String yourSQL="insert into Table1(Id,Col2,Col3) values(SEQ.nextval,?,?)";
//IMPORTANT: here's where other threads don tell U, you need to list ALL cols
//mentioned in your query in the array
myPrepStatement = <Connection>.prepareStatement(yourSQL, new String[]{"Id","Col2","Col3"});
myPrepStatement.setInt(1, 123);
myPrepStatement.setInt(2, 123);
myPrepStatement.executeUpdate();
ResultSet rs = getGeneratedKeys;
if(rs.next()) {
//In this exp, the autoKey val is in 1st col
int id=rs.getLong(1);
//now this's a real value of col Id
System.out.println(id);
}
} catch (SQLException e) {
}
基本上,如果你只想要SEQ.Nextval的值,请尝试不使用Method1,b'cse它只返回RowID引用,你可能会破解你的头部找到使用它的方法,这也适合所有数据类型你试过把它投到了!这可能在MySQL,DB2中正常工作(返回实际的val),但在Oracle中却没有。
重要: 在调试时关闭SQL Developer,Toad或使用相同登录会话的任何客户端进行INSERT。它可能不会影响你每次(调试调用)...直到你发现你的应用程序冻结一段时间没有异常。是的......毫无例外地停止!