问题
When using a UUID data type as the primary key of a table, and asking H2 to generate each UUID value by default upon INSERT of a new record, how does one access the value of the newly generated UUID value?
I am using plain JDBC 4.x in a Java app, if that helps with a solution.
I know SCOPE_IDENTITY function returns a long
for a key generated on a column marked as IDENTITY
for an auto-incrementing sequence number. But I am using UUID rather than an incrementing number as my primary key column types.
回答1:
Specify name(s) of generated columns
When preparing your statement, you can optionally pass an array of the names of columns for which a default value is being generated. For just your single primary key column, that means an array of one value.
See the method:
Connection::prepareStatement( String sql, String[] columnNames )
Example code.
// here you can specify the list of returned attributes, in your case just the data
String[] returnedAttributes = {"data"};
String insertQuery = "insert into test(id) values(1);";
try
(
PreparedStatement insertStatement = conn.prepareStatement(insertQuery, returnedAttributes);
)
{
int rows = insertStatement.executeUpdate();
if (rows == 0)
{
throw new SQLException("Failed of insertion");
}
try (ResultSet rs = insertStatement.getGeneratedKeys()) {
if (rs.next())
{
java.util.UUID uuid = (java.util.UUID) rs.getObject("data");
System.out.println(uuid);
}
}
}
Note
to get the UUID type you have to use getObjct(..)
and cast it to java.util.UUID
as stated in the documentation of H2 UUID Type
Universally unique identifier. This is a 128 bit value. To store values, use PreparedStatement.setBytes, setString, or setObject(uuid) (where uuid is a java.util.UUID). ResultSet.getObject will return a java.util.UUID.
My example based on the link you shared in your question Generate UUID values by default for each row on column of UUID type in H2 Database Engine
回答2:
Statement::getGeneratedKeys
As seen in comments and the correct Answer by YCF_L, the solution lies in standard JDBC: Call Statement::getGeneratedKeys. This yields a ResultSet of the key values generated by default in the previous use of that statement. This works with PreparedStatement, and works with auto-generating UUID values as the primary key.
Statement.RETURN_GENERATED_KEYS
The catch is that by default you do not get back generated keys. You must activate this feature by passing an extra argument to your Connection::prepareStatement call. The extra argument is an int
, using a constant defined on the Statement
interface, Statement.RETURN_GENERATED_KEYS. In modern Java that would have likely have been defined as an Enum, but JDBC dates back to the earliest days of Java, so the argument is a simple int
.
Example app
Here is a complete example app, in a single file.
package work.basil.example.h2.auto_uuid;
import java.sql.*;
import java.util.UUID;
public class App {
public static void main ( String[] args ) {
App app = new App();
app.doIt();
}
private void doIt ( ) {
try {
Class.forName( "org.h2.Driver" );
} catch ( ClassNotFoundException e ) {
e.printStackTrace();
}
try (
Connection conn = DriverManager.getConnection( "jdbc:h2:mem:auto_uuid_example_db;DB_CLOSE_DELAY=-1" ) ; // Set `DB_CLOSE_DELAY` to `-1` to keep in-memory database in existence after connection closes.
Statement stmt = conn.createStatement() ;
) {
String sql = "CREATE TABLE person_ ( \n" +
" pkey_ UUID NOT NULL DEFAULT RANDOM_UUID() PRIMARY KEY , \n" +
" name_ VARCHAR NOT NULL \n" +
");";
stmt.execute( sql );
// Insert row.
sql = "INSERT INTO person_ ( name_ ) \n";
sql += "VALUES ( ? ) \n";
sql += ";";
try (
PreparedStatement pstmt = conn.prepareStatement( sql , Statement.RETURN_GENERATED_KEYS ) ;
) {
pstmt.setString( 1 , "Jesse Johnson" );
pstmt.executeUpdate();
ResultSet rs = pstmt.getGeneratedKeys();
System.out.println( "INFO - Reporting generated keys." );
while ( rs.next() ) {
UUID uuid = rs.getObject( 1 , UUID.class );
System.out.println( "generated keys: " + uuid );
}
}
// Dump all rows.
System.out.println( "INFO - Reporting all rows in table `person_`." );
sql = "SELECT * FROM person_";
try ( ResultSet rs = stmt.executeQuery( sql ) ; ) {
while ( rs.next() ) {
UUID pkey = rs.getObject( "pkey_" , UUID.class );
String name = rs.getString( "name_" );
System.out.println( "Person: " + pkey + " | " + name );
}
}
} catch ( SQLException e ) {
e.printStackTrace();
}
}
}
When run.
INFO - Reporting generated keys.
generated keys: 9c6ce984-151b-4e64-8334-d96e17be9525
INFO - Reporting all rows in table
person_
.Person: 9c6ce984-151b-4e64-8334-d96e17be9525 | Jesse Johnson
If you want to insert multiple rows at a time, rather than one, use batching. See: Java: Insert multiple rows into MySQL with PreparedStatement.
If you have multiple columns being auto-generated, rather than just the one single UUID column seen here, see the other Answer by YCF_L.
来源:https://stackoverflow.com/questions/53907038/return-primary-key-value-generated-by-default-in-h2-database-upon-insert-of-new