views:

389

answers:

3

I have two sql insert to do (say for examples in tables A and B), they are in a transaction because I want the database to remain consistent, that is, a tuple in A must have references in B.

In the second insert I need the id that comes from the first, but I don't get this id until I make a commit on the transaction.
So I'm stuck. I don't want to take the first insert out of the transaction, it may happen that the first insert goes ok but the second does not, leaving me with an inconsistent state in the database.

What is best practice in this situation?

EDIT: Here is the code:

TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
try{
    Integer aId = insertIntoA();
    insertIntoB(aId);
}catch(){
    transactionManager.rollback(txStatus);
    throw new CustomException(); 
}
transactionManager.commit(txStatus);

What I want to point out is that I do not get aId until I commit the transaction, therefore inserting null into B.

+1  A: 

What id are you looking at ? You should be able to get (say) primary key ids autogenerated via sequences (a common scenario) regardless of a transaction status. Can you post some code to clarify ?

Brian Agnew
I edited my question to include example code.
Alberto Zaccagni
Could you expand on that "regardless of a transaction status" please? Especially in a case like mine with Spring.
Alberto Zaccagni
A: 

After your first insert, can you do

SELECT @@IDENTITY

To get the identity of the row you just inserted?

Paul
oops sorry - thats for ms sql...
Paul
I've done that in iBatis level, my problem is that aId is null since the query has not yet been done (the code is in a transaction)
Alberto Zaccagni
+2  A: 

On MySQL, in insertIntoA you should be able to do:

SELECT LAST_INSERT_ID()

...on the same connection you used for the insert, assuming it's an identity column value you're looking for.

EDIT: If you're doing that and it's not working (per your comment), I'd look at the middle layers to see what's going on. MySQL is fine with it:

mysql> create table A (id int(11) not null auto_increment, descr varchar(64), primary key (id));
Query OK, 0 rows affected (0.13 sec)

mysql> create table B (fk int(11) not null, descr varchar(64));
Query OK, 0 rows affected (0.06 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into A (descr) values ('Testing 1 2 3');
Query OK, 1 row affected (0.00 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                1 |
+------------------+
1 row in set (0.03 sec)

mysql> insert into B (fk, descr) values (1, 'Test complete');
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.03 sec)

mysql> select * from A;
+----+---------------+
| id | descr         |
+----+---------------+
|  1 | Testing 1 2 3 |
+----+---------------+
1 row in set (0.02 sec)

mysql> select * from B;
+----+---------------+
| fk | descr         |
+----+---------------+
|  1 | Test complete |
+----+---------------+
1 row in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into A (descr) values ('Second test');
Query OK, 1 row affected (0.01 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)

mysql> insert into B (fk, descr) values (2, 'Second test complete');
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.08 sec)

mysql> select * from A;
+----+---------------+
| id | descr         |
+----+---------------+
|  1 | Testing 1 2 3 |
|  2 | Second test   |
+----+---------------+
2 rows in set (0.02 sec)

mysql> select * from B;
+----+----------------------+
| fk | descr                |
+----+----------------------+
|  1 | Test complete        |
|  2 | Second test complete |
+----+----------------------+
2 rows in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into A (descr) values ('We''ll roll this one back.');
Query OK, 1 row affected (0.00 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                3 |
+------------------+
1 row in set (0.00 sec)

mysql> insert into B (fk, descr) values (3, 'Won''t see this one.');
Query OK, 1 row affected (0.00 sec)

mysql> select * from B;
+----+----------------------+
| fk | descr                |
+----+----------------------+
|  1 | Test complete        |
|  2 | Second test complete |
|  3 | Won't see this one.  |
+----+----------------------+
3 rows in set (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.03 sec)

mysql> select * from A;
+----+---------------+
| id | descr         |
+----+---------------+
|  1 | Testing 1 2 3 |
|  2 | Second test   |
+----+---------------+
2 rows in set (0.00 sec)

mysql> select * from B;
+----+----------------------+
| fk | descr                |
+----+----------------------+
|  1 | Test complete        |
|  2 | Second test complete |
+----+----------------------+
2 rows in set (0.00 sec)
T.J. Crowder
I've already done that, that is how I do it usually but in this case it is different I think. I do not get aId in the assignment in the above code because the transaction has not yet been commited. Does this makes sense?
Alberto Zaccagni
Ok, I know that transactions in mysql work as expected, but, I have Spring in the middle, that's the difference I think.
Alberto Zaccagni
Probably. Good luck with it.
T.J. Crowder
Luck is what I need to prevent me taking the first insert out of the transaction :P Thank you by the way.
Alberto Zaccagni
Ok solved, I just made a stupid error myself.
Alberto Zaccagni