# Need advice: transaction block for saving?

**URL:** <https://www.ruby-forum.com/t/need-advice-transaction-block-for-saving/112164>\
**Category:** Rails\
**Created:** [August 16, 2007, 12:51am UTC](https://www.ruby-forum.com/t/need-advice-transaction-block-for-saving/112164 "2007-08-16T00:51:01Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Dcjgoose](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e47774/32.png) [@Dcjgoose](https://www.ruby-forum.com/u/Dcjgoose)\
**Post date:** [August 16, 2007, 12:51am UTC](https://www.ruby-forum.com/t/need-advice-transaction-block-for-saving/112164/1 "2007-08-16T00:51:01Z")

</div>

I am building an app to allow clients to create a project and fill out  
a survey. There are also contractors and hiring\_managers associated  
with the project. Below are the models:

class User \< ActiveRecord::Base  
has\_many :projects\_as\_client, :class\_name=\>  
‘Project’, :foreign\_key =\> ‘client\_id’  
has\_many :projects\_as\_contractor, :class\_name=\>  
‘Project’, :foreign\_key =\> ‘contractor\_id’  
has\_many :projects\_as\_hiring\_manager, :class\_name=\>  
‘Project’, :foreign\_key =\> ‘hiring\_manager\_id’  
end

class Project \< ActiveRecord::Base  
belongs\_to :client, :class\_name =\> ‘User’, :foreign\_key =\>  
‘client\_id’  
belongs\_to :contractor, :class\_name =\> ‘User’, :foreign\_key =\>  
‘contractor\_id’  
belongs\_to :hiring\_manager, :class\_name =\> ‘User’, :foreign\_key  
=\> ‘hiring\_manager\_id’

When I create the project, I may also be creating a contractor and  
hiring\_manager, etc. What is the best way to handle this? I read about  
using something like this:

@project=Project.new(params[:user])  
@project.contractor = @project.contractor.build(params[:user])

Unfortunately, that also gives me an Unknown Method error. A friend  
suggested using a transaction block so that if there is an error, all  
saves/updates would be rolled back.

Thanks in advance from a newbie!

---

<div class="post-metadata">

**Author:** ![Dcjgoose](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e47774/32.png) [@Dcjgoose](https://www.ruby-forum.com/u/Dcjgoose)\
**Post date:** [August 17, 2007, 12:01pm UTC](https://www.ruby-forum.com/t/need-advice-transaction-block-for-saving/112164/2 "2007-08-17T12:01:29Z")

</div>

Dcjgoose wrote:

> belongs\_to :client, :class\_name =\> ‘User’, :foreign\_key =\>
> 
> @project=Project.new(params[:user])  
> @project.contractor = @project.contractor.build(params[:user])
> 
> Unfortunately, that also gives me an Unknown Method error. A friend  
> suggested using a transaction block so that if there is an error, all  
> saves/updates would be rolled back.

It’s best not to rely on rollback, both for efficiency and in case  
the DB doesn’t support transactions or suitable transaction isolation.

Correct way to write the above line is

@project.contractor = @project.build\_contractor(params[:user])

Just ensure everything is validated before anything is saved.

–  
We develop, watch us RoR, in numbers too big to ignore.

---

<div class="post-metadata">

**Author:** ![Dcjgoose](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e47774/32.png) [@Dcjgoose](https://www.ruby-forum.com/u/Dcjgoose)\
**Post date:** [August 17, 2007, 2:29pm UTC](https://www.ruby-forum.com/t/need-advice-transaction-block-for-saving/112164/3 "2007-08-17T14:29:44Z")

</div>

On 8/17/07, Mark Reginald J. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Dcjgoose wrote:
> 
> > […] A friend  
> > suggested using a transaction block so that if there is an error, all  
> > saves/updates would be rolled back.
> 
> It’s best not to rely on rollback, both for efficiency and in case  
> the DB doesn’t support transactions or suitable transaction isolation.

I’m sorry, but this is shitty advice.

Transactions are the only way to avoid partial updates. They also make  
your program flow a lot simpler than trying to without them (which  
doesn’t really work anyway).

Any db worth using supports transactions, and performance tends to be  
worse in auto-commit mode, when every single statement is run as a  
separate transaction, instead of bulking all writes into a single  
commit.

Granted, the most popular ‘database’, mysql, takes a bit of know-how  
and reconfiguration to support transactions andact somewhat  
well-behaved.  
I recommend PostgreSQL instead; not only is it superior in every  
respect, but also easier to get started with…!

Isak

---

<div class="post-metadata">

**Author:** ![Dcjgoose](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e47774/32.png) [@Dcjgoose](https://www.ruby-forum.com/u/Dcjgoose)\
**Post date:** [August 17, 2007, 4:30pm UTC](https://www.ruby-forum.com/t/need-advice-transaction-block-for-saving/112164/4 "2007-08-17T16:30:58Z")

</div>

Isak H. wrote:

> Transactions are the only way to avoid partial updates. They also make  
> your program flow a lot simpler than trying to without them (which  
> doesn’t really work anyway).

A transaction block should always be used when an action saves  
multiple objects so that save errors unrelated to validation do not  
result in partial inserts/updates on DBs that support transactions.  
Indeed Rails automatically wraps saves of associated objects in  
transactions.

But it’s not that hard to validate everything before saving anything,  
avoiding the latency of all those useless database calls, reducing  
server load and returning error messages to the user much faster.

–  
We develop, watch us RoR, in numbers too big to ignore.

---

<div class="post-metadata">

**Author:** ![Dcjgoose](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/e47774/32.png) [@Dcjgoose](https://www.ruby-forum.com/u/Dcjgoose)\
**Post date:** [August 17, 2007, 4:27pm UTC](https://www.ruby-forum.com/t/need-advice-transaction-block-for-saving/112164/5 "2007-08-17T16:27:58Z")

</div>

Just to reiterate the point I wrote a lot of code for MySQL before it  
hadd InnoDB support that looked like the following psuedo code:

exec\_sql "Insert into table1 values (‘a’, ‘b’, ‘c’)  
my\_id = exec\_sql “select last\_id from db”

exec\_sql "Insert into table2 values (my\_id, 1, 2, 3)  
next\_id = exec\_sql “select last\_id from db”

if $previous\_query\_error  
exec\_sql(“Delete from table1 where id = $my\_id”)  
else  
exec\_sql insert into table3 values (next\_id, ‘done’)  
if $previous\_query\_error  
exec\_sql(“Delete from table2 where id = $next\_id”)  
exec\_sql(“Delete from table1 where id = $my\_id”)  
end  
end

This is what you are forced to do without transactions. That kind of  
code adds no value to your application. And it works assuming that  
there isn’t a problem executing the clean-up code and orphaned child  
records are left around. In addition transactions can improve  
performance. If you are inserting/updating multiple records the  
writing to the database is essentially (at the 30,000 foot level)  
postponed until the end of the transaction. That’s why tools like  
Oracle data loader allow you to specify transaction batch sizes, to  
optimize the speed of the load against the space/memory available for  
transactions.

MySQL  
PostgreSQL  
SQL Server  
Oracle  
Sybase

All support transactions, and they cover the vast majority of the DB  
market.

The difficulty comes when you’re using something that doesn’t support  
rollback, like a LDAP server. In which case you are still required  
to write clean-up code.
