Showing posts with label identity. Show all posts
Showing posts with label identity. Show all posts

Tuesday, March 27, 2012

Auto Commit off and getGeneratedKeys

I have AutoCommit turned off, as I am managing commit and rollback.

I also need to retrieve an identity key. Using the statement.getGeneratedKeys throws the error "The statement must be run before the generated keys are avalilable".

Ok, I can understand this, as I am trying to get the identity key before I am commiting the transaction. However I am inserting the parent row, then based on the identity key, I am inserting several children, all within the same transaction.

So how do I do this?

I d/l the latest JDBC drivers and using Java 1.5Sooooo, I need to commit the parent, then do the children within a transaction, and if anything blows up I rollback the children, then do an explicit delete on the parent? What if the app blows up, I now have a parent without the requisit children.

Sort of defeats the whole transaction thingy.|||

Hi busybee123,

I am a little confused about what you are trying to accomplish. Some sample/pseudo code would be great for helping us understand your algorithm. Are you trying to perform nested transactions or are you using two separate connection objects?

Here is some pseudo code of what I suspect you what to achieve; correct me if I am mistaken:

Connection conn1 = DriverManager.getConnection(...);

Connection conn2 = DriverManager.getConnection(...);

conn1.setAutoCommit(false);

conn2.setAutoCommit(false);

Statement stmt1 = conn1.createStatement();

Statement stmt2 = conn2.createStatement();

String parentUpdates[] = new String[]{all parent updates};

boolean commitParent;

for(int i = 0; i < parentUpdates.length; i++)

{

commitParent = true;

stmt1.executeUpdate(parentUpdates[ i ], Statement.RETURN_GENERATED_KEYS);

ResultSet keys = stmt1.getGeneratedKeys();

while(keys.next())

{

String aKey = keys.getString(1);

stmt2.addBatch(insert the children here given aKey value);

}

try{

int updateCounts[] = stmt2.executeBatch();

}

catch(BatchUpdateException bue)

{

//process success/failures here and decide if to commit conn1 and/or conn2 or not

if(something went wrong)

{

conn2.rollback();

commitParent = false;

}

else

{

conn2.commit();

}

}

if(commitParent)

{

conn1.commit(); //or delete parent now

}

else

{

//break, continue, or throw here

}

}

Thanks,

Jaaved Mohammed - MSFT

|||A few things contributed to my post.

We were using an older JDBC driver which did not support getGeneratedKeys(), so we were using "insert ...;select SCOPE_IDENTITY() as newkey" with a statement.executeQuery(). However when we started using manual commit/rollback, this does not work as it creates an internal virtual connection which cannot be used with manual commit.

So we upgraded to the latest JDBC driver which does support getGeneratedKeys(). Reading through the new documentation we came across your example code. When we tried it out, we got the above noted error message. Searching the web for the error message produced zero hits.

Eventually we found that the statement.executeUpdate() method can take a second parameter with the value of Statement.RETURN_GENERATED_KEYS. This causes MS SQL to return the generated key which can then be retrieved as per the examples. So our bad :-) In retrospect the error message does explain what happened, but hindsight is always 20/20

A few things:
- the error message returned was not entirely clear as to why it was failing, and the error code number was zero.

- No documentation about the error could be found.

- in the HTML documentation under "Using Auto Generated Keys", in the second paragraph, second to last sentence it states "with the returned column name of GENERATED_KEYS". This is incorrect. Inspecting the metadata of the result set shows that the column does not have any name or label (empty string). The value can only be retrieved by using the index number of 1.

- could the error messages produced by the JDBC library be documented please?

Kudos to the JDBC team for supporting getGeneratedKeys()!|||I'm working on a similar issue and this code seems to help a bit, but i have a question : does getGeneratedKeys not work with a PreparedStatement for which i'm using batch update? I have my code on this post: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1470935&SiteID=1sql

Auto Commit off and getGeneratedKeys

I have AutoCommit turned off, as I am managing commit and rollback.

I also need to retrieve an identity key. Using the statement.getGeneratedKeys throws the error "The statement must be run before the generated keys are avalilable".

Ok, I can understand this, as I am trying to get the identity key before I am commiting the transaction. However I am inserting the parent row, then based on the identity key, I am inserting several children, all within the same transaction.

So how do I do this?

I d/l the latest JDBC drivers and using Java 1.5
Sooooo, I need to commit the parent, then do the children within a transaction, and if anything blows up I rollback the children, then do an explicit delete on the parent? What if the app blows up, I now have a parent without the requisit children.

Sort of defeats the whole transaction thingy.|||

Hi busybee123,

I am a little confused about what you are trying to accomplish. Some sample/pseudo code would be great for helping us understand your algorithm. Are you trying to perform nested transactions or are you using two separate connection objects?

Here is some pseudo code of what I suspect you what to achieve; correct me if I am mistaken:

Connection conn1 = DriverManager.getConnection(...);

Connection conn2 = DriverManager.getConnection(...);

conn1.setAutoCommit(false);

conn2.setAutoCommit(false);

Statement stmt1 = conn1.createStatement();

Statement stmt2 = conn2.createStatement();

String parentUpdates[] = new String[]{all parent updates};

boolean commitParent;

for(int i = 0; i < parentUpdates.length; i++)

{

commitParent = true;

stmt1.executeUpdate(parentUpdates[ i ], Statement.RETURN_GENERATED_KEYS);

ResultSet keys = stmt1.getGeneratedKeys();

while(keys.next())

{

String aKey = keys.getString(1);

stmt2.addBatch(insert the children here given aKey value);

}

try{

int updateCounts[] = stmt2.executeBatch();

}

catch(BatchUpdateException bue)

{

//process success/failures here and decide if to commit conn1 and/or conn2 or not

if(something went wrong)

{

conn2.rollback();

commitParent = false;

}

else

{

conn2.commit();

}

}

if(commitParent)

{

conn1.commit(); //or delete parent now

}

else

{

//break, continue, or throw here

}

}

Thanks,

Jaaved Mohammed - MSFT

|||A few things contributed to my post.

We were using an older JDBC driver which did not support getGeneratedKeys(), so we were using "insert ...;select SCOPE_IDENTITY() as newkey" with a statement.executeQuery(). However when we started using manual commit/rollback, this does not work as it creates an internal virtual connection which cannot be used with manual commit.

So we upgraded to the latest JDBC driver which does support getGeneratedKeys(). Reading through the new documentation we came across your example code. When we tried it out, we got the above noted error message. Searching the web for the error message produced zero hits.

Eventually we found that the statement.executeUpdate() method can take a second parameter with the value of Statement.RETURN_GENERATED_KEYS. This causes MS SQL to return the generated key which can then be retrieved as per the examples. So our bad :-) In retrospect the error message does explain what happened, but hindsight is always 20/20

A few things:
- the error message returned was not entirely clear as to why it was failing, and the error code number was zero.

- No documentation about the error could be found.

- in the HTML documentation under "Using Auto Generated Keys", in the second paragraph, second to last sentence it states "with the returned column name of GENERATED_KEYS". This is incorrect. Inspecting the metadata of the result set shows that the column does not have any name or label (empty string). The value can only be retrieved by using the index number of 1.

- could the error messages produced by the JDBC library be documented please?

Kudos to the JDBC team for supporting getGeneratedKeys()!
|||I'm working on a similar issue and this code seems to help a bit, but i have a question : does getGeneratedKeys not work with a PreparedStatement for which i'm using batch update? I have my code on this post: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1470935&SiteID=1

Monday, March 19, 2012

Auotincrement without using autoincrement

What do you have against using IDENTITY in this case? The trouble is that
without it you will have to serialize every INSERT, which is likely to be
unacceptable in a multi-user system.
David Portas
SQL Server MVP
--David Portas wrote:
> The trouble is that
> without it (IDENTITY) you will have to serialize every INSERT, which is li
kely to be
> unacceptable in a multi-user system.
It is not really multi-user in the sense you may be thinking. It is a
system which accepts FTP transfers and performs tasks on the received
files. The FTP service I wrote is multi-threaded. Problem is that many
automated clients all like to set up their transfers to run at 1 AM, for
example, so depending on how accurate everyone's clocks are, and how
similar the transmission files are in size, I can easily get 3 additions
made at almost the same time. When there may only be 30-100 additions
total per 24-hour period. I don't need it to be fast, but rather, just
am trying to avoid the primary key constraint issue, if I can, without
error. Obviously I could "try again" if I get an error first time around
from something like
INSERT INTO NMJobTransfers
(WorkID,TransferID,JobID,FileCount,ByteC
ount,Comments)
SELECT MAX(WorkID)+1, 99, 0 ,0 , 0, NULL FROM NMJobTransfers
BUT TO BE SURE I UNDERSTAND your implication, you are saying the above
approach is still going to open a very small window of time where there
could be a duplicate key error on the WorkID primary key column? Is that
correct?
- Lee|||If you set transaction isolation SERIALIZABLE you won't get a duplicate.
What you will get is blocking of simultaneous INSERTs. That is exactly the
problem that the IDENTITY feature is designed to solve. And that's why I
asked why you aren't using IDENTITY to do this.
David Portas
SQL Server MVP
--|||Actually, I am pretty sure that even using serializable you have to do
tricks with locks to make sure that there is no chance of two people getting
the same value because the read is done first, then the insert, so if two
users start looking for the max(nextval) at the same time, they both succeed
unless you use hold exclusive locks on the max value.
----
Louis Davidson - http://spaces.msn.com/members/drsql/
SQL Server MVP
"David Portas" <REMOVE_BEFORE_REPLYING_dportas@.acm.org> wrote in message
news:wIadnZ2dnZ1RIDm4nZ2dnWKlZN-dnZ2dRVn-zZ2dnZ0@.giganews.com...
> If you set transaction isolation SERIALIZABLE you won't get a duplicate.
> What you will get is blocking of simultaneous INSERTs. That is exactly the
> problem that the IDENTITY feature is designed to solve. And that's why I
> asked why you aren't using IDENTITY to do this.
> --
> David Portas
> SQL Server MVP
> --
>