I'm working with JDBC and HSQLDB 2.2.9. What's the most efficient and accurate way to insert a new row into a DB and, subsequently, retain its id
(PK set to autoincrement) value? The reason I need to do this is probably pretty obvious, but I'll illustrate with an example for discussion:
Say there's a Customer
table that has a PersonId
field with a FK constraint referring to a row from a Person
table. I want to create a new Customer
, but to do this I need to first create a new Person
and use the new Person.id
value to set Customer.PersonId
.
I've seen four ways to approach this:
Insert the
Person
row setting theid
field tonull
. HSQLDB generates the nextid
value automatically. Then perform a query on thePerson
table to get theid
value just created and use it to create the newCustomer
row.This seems expensive just to retrieve a single integer value.
Get the next
id
value in thePerson
table and use it in theINSERT
statement to set thePerson.id
value manually. Use the sameid
value to setCustomer.PersonId
. No subsequent read from the DB is needed.Inconsistencies may arise if an
id
value is obtained, but another connection performs anINSERT
in the table before myINSERT INTO Person...
statement is executed.Execute the
INSERT
statement, as in option 1 above, settingid=null
to allow auto-generation. Then use thegetGeneratedKeys
method to retrieve keys generated in last statement.I thought this sounded like a good option, but I couldn't get it to work. Here's a snippet of my code:
// PreparedStatement prepared previously... preparedStatement.executeUpdate(); ResultSet genKeys = preparedStatement.getGeneratedKeys(); int id; if (genKeys.next()) { id = genKeys.getInt(1); } // Finish up method...
This code was returning an empty
ResultSet
forgenKeys
. Am I using thegetGeneratedKeys
method incorrectly? If I could get this to work, this might be the way to go.Again, execute the
INSERT
statement allowing for auto-generatedid
. Then immediately executeCALL IDENTITY()
to retrieve the lastid
value generated by the connection (as explained here and mentioned in this SO question).This also seems like a reasonable option, even though I must perform an additional
executeQuery
. On the positive side, I was actually able to get it to work with the following code:// INSERT statement executed here... statement = connection.createStatement(); ResultSet rs = statement.executeQuery("CALL IDENTITY();"); int id; if (rs.next()) id = rs.getInt(1); // Finish up method...
So, in summary, the first two options I'm not crazy about. The second two seem ok, but I could only get option 4 to work. Which option is preferred and why? If option 3 is the best, what am I doing wrong? Also, is there a better way that I haven't mentioned? I know words like 'better' can be subjective, but I'm working with a simple DB and want the most direct solution that doesn't open up the DB to possible inconsistencies and doesn't increase the transaction failure rate (due to trying to create a record with an id
that already exists).
This seems like a basic question (and essential), but I couldn't find much guidance on the best way to do it. Thanks.
EDIT: I just found this question that discusses my option 3. According to the accepted answer, it appears I was leaving out the
Statement.RETURN_GENERATED_KEYS
parameter needed to enable that functionality. I didn't show the prepareStatement
method in my code snippet, but I was using the single parameter version. I need to retry using the overloaded, two-parameter version.
There are also a few other SO questions which show up with that question that are closly related to my question. So, I guess mine could be considered a duplicate (not sure how I missed those other questions before). But I'd still like any guidance on whether one solution is considered better than the others. For now, if I get option 3 to work, I'll probably go with that.