I need to create two model in a single transaction: if the first
fails, the transaction should be rolled back; if the second fails, the
transaction should be rolled back.
This is my code:
1. begin ActiveRecord::Base.transaction do
2. @first_model = FirstModel.new(params[:first_model])
3. @first_model.save!
4. @second_model = SecondModel.new(:user =>
current_user, :first_model => @first_model)
5. @second_model.save!
6. end
7. rescue Exception => @ex
8. # handle the exception
9. end
The global rollback works only if the "@first_model.save!" fails.
If the "@second_model.save!" fails, the global transaction isn't
rolled back, and the first_model is still saved on database.
I've also tried using nested transactions:
1. begin
2. FirstModel.transaction do
3. SecondModel.transaction do
4. @first_model = FirstModel.new(params[:first_model])
5. @first_model.save!
6. @second_model = SecondModel.new(:user =>
current_user, :first_model => @first_model)
7. @second_model.save!
8. end
9. end
10.rescue Exception => @ex
11. # handle the exception
12.end
but also this solutions shows the same problems as above.
How can I solve my issue?
Is there a way to define a sort of global transaction in Rails?
I need to create two model in a single transaction: if the first
fails, the transaction should be rolled back; if the second fails, the
transaction should be rolled back.
This is my code:
1. begin ActiveRecord::Base.transaction do
2. @first_model = FirstModel.new(params[:first_model])
3. @first_model.save!
4. @second_model = SecondModel.new(:user =>
current_user, :first_model => @first_model)
5. @second_model.save!
6. end
7. rescue Exception => @ex
8. # handle the exception
9. end
The global rollback works only if the "@first_model.save!" fails.
If the "@second_model.save!" fails, the global transaction isn't
rolled back, and the first_model is still saved on database.
Are testing this from a unit test or in development mode ?
I've also tried using nested transactions:
(rails doesn't support nested transactions. the second and following transactions are basically no-ops)
This code throws an exception due to the string "mickeymouse" and this
second save is rolled back. But the first save isn't rolled back and
the @first_model is saved in my database.
PS: I'm using MySql 5 and Rails 2.0.2
This code throws an exception due to the string "mickeymouse" and this
second save is rolled back. But the first save isn't rolled back and
the @first_model is saved in my database.
PS: I'm using MySql 5 and Rails 2.0.2
That's odd. I suspect what is happening (check this in your logs) is
that the first save is being wrapped in a transaction, but since you
already have a transaction going, starting that transaction implicitly
commits the first. However rails should notice that you are already in
a transaction and not do this.
It appears that save! throws an exception upon failure and is
supposed to trigger a rollback. You are rescuing that exception.
Maybe try it without the rescue.
The transaction is only on the @first_model which it appears is not
cascading to the @second_model
Maybe instead of save!, use the save message which returns false
instead of raising an exception.
begin
@first_model = FirstModel.new(params[:first_model])
@second_model = SecondModel.new(:user =>
current_user, :first_model => @first_model)
if @first_model.save and @second_model.save
logger.warn "saved"
else
logger.warn "this should rollback"
end
rescue Exception => e
end
I've placed in a single if statement all the saves "if
@first_model.save and @second_model.save" and all works, but I really
don't like this approach.
And I don't understand WHY Rails and ActiveRecords cannot let the
developer to handle complex transactions by himself.