How can I control when to commit a transaction?


#1

hello

it seems like this question has appeared a few times with no answer:

how do you get rails to issue ‘write’ statements (ie ‘CREATE/UPDATE’) to
the database without committing each time?

I tried setting

ActiveRecord::Base.connection.begin_db_transaction

before calling any action on my object, but as soon as

myObject.save
or
myObject.update

is called, rails will issue a ‘commit’ after the ‘CREATE’ or ‘UPDATE’.

The example given in the rails doc does not work for my application
because I cannot enclose my actions in a block (the user is allowed to
call several methods in the controller before committing).

So far I had to use update_all with a where condition. Are there other
methods that will issue an UPDATE statement without immediately sending
a COMMIT?

thanks for any hints

Isabelle


#2

On Jun 1, 2006, at 9:00 AM, iphan iphan wrote:

The example given in the rails doc does not work for my application
because I cannot enclose my actions in a block (the user is allowed to
call several methods in the controller before committing).

So far I had to use update_all with a where condition. Are there other
methods that will issue an UPDATE statement without immediately
sending
a COMMIT?

Long-running transactions in web apps are painfully unnecessary: keep
the record under edit in session; save it when the user chooses to
commit; employ optimistic locking to detect and resolve conflicts.

If you insist on riding the client/server dinosaur and can tolerate
coding against a fluid private API :wink: then do
model.save_without_transactions(true)

jeremy


#3

thanks Jeremy, this is very helpful.

keep
the record under edit in session; save it when the user chooses to
commit; employ optimistic locking to detect and resolve conflicts.

is it possible to perform validation with this method? The scenario: my
object has several 1-n associations

  • user edits object itself
  • ‘update’ saves record without commit if it is valid
  • user edits associated objects
  • …etc…
  • ‘save’ commits to database / ‘undo all’ restores previous state

validation on step2 would prevent further edits to be lost if the commit
fails over a constraint violation.

The reason I do not want to commit each time: I need to have the
application behave like a text editor, i.e. nothing is kept if the work
is not saved. My users have requested that they can control when to
save and when not.

This is so that other users can follow the changes, just like in a cvs
repository. And in this case, one does not commit after every single
change.

I guess the difference is that my object is not a single file, but
several tables :frowning:

Isabelle


#4

Jeremy K. wrote:

If you insist on riding the client/server dinosaur and can tolerate
coding against a fluid private API :wink: then do
model.save_without_transactions(true)

this method is not documented anywhere. I did a search on the Edge
documentation and could not find it.


#5

Isabelle, you’ll want to do something like this…

(snippet from order saving in my project substruct)

Does a creation of all required objects from a form post

Each model is created and validated at the beginning.

This assures all errors show up if even if the begin…rescue…end

block skips save! of a model.

Does transaction to create a new order.

Will throw an exception if there is a problem, so be sure to handle

that
def create_order_from_post
@use_separate_shipping_address =
params[:use_separate_shipping_address]

@order_user = OrderUser.new(params[:order_user])
@order_user.valid?

@order = Order.new(params[:order])
@order.valid?

@billing_address = OrderAddress.new(params[:billing_address])
@billing_address.valid?

@shipping_address = OrderAddress.new(params[:shipping_address])
if @use_separate_shipping_address
  @shipping_address.valid?
end

@order_account = OrderAccount.new(params[:order_account])
@order_account.valid?

OrderUser.transaction do
  @order_user.save!
  Order.transaction do
    @order = @order_user.orders.build(params[:order])
    @order.order_number = Order.generate_order_number
    @order.save!
  end
  OrderAddress.transaction do
    # Addresses
    @billing_address =

@order_user.order_addresses.create(params[:billing_address])
@billing_address.is_shipping = true
@billing_address.save!
if @use_separate_shipping_address then
@shipping_address =
@order_user.order_addresses.create(params[:shipping_address])
@shipping_address.is_shipping = true
@billing_address.is_shipping = false
@billing_address.save!
@shipping_address.save!
end
end
OrderAccount.transaction do
@order_account = OrderAccount.new(params[:order_account])
@order_account.order_user_id = @order_user.id
@order_account.order_address_id = @billing_address.id
@order_account.save!
end
end
end