How can transactions be utilized in Ruby DataMapper?
-
21-01-2021 - |
Question
I have a class that executes SQL statements (updates, inserts, and deletes) that are stored in a YAML file. I would like all the statements to be part of one transaction. If any of the SQL statements fail, then they would be rolled back. If all of the statements are successful, then they would be committed. I'm connecting to a MySQL database. Here is my code:
require 'dm-core'
class SqlExecuter
def initialize(input_yaml_file_name)
@input_yaml_file_name = input_yaml_file_name
@adapter = DataMapper.repository(:default).adapter
@sql_statements = YAML::load(File.open(input_yaml_file_name))
end
def execute()
puts "Executing SQL statements in #{@input_yaml_file_name} file...."
@sql_statements.each do | sql_statement |
@adapter.execute(sql_statement)
end
end
end # class SqlExecuter
I'd like to have all of my @adapter.execute calls be part of one transaction. I've looked at the code in the dm-transactions gem, but I can't figure out how to use it in this context.
Solution
Use this to embody your SQL statements in a transaction and rollback if an error occurs:
require 'dm-transactions'
YourModel.transaction do |t|
begin
@sql_statements.each do |sql_statement|
DataMapper.repository(:default).adapter.execute(sql_statement)
end
rescue DataObjects::Error
t.rollback
end
end
Take a look at Using transactions with Ruby DataMapper and dm-transactions_spec.rb
OTHER TIPS
As far as I can tell, you no longer need to call rollback()
for a transaction to be rolled back. You merely need to enclose it in a transaction block, like so:
YourModel.transaction do
@sql_statements.each do |sql_statement|
DataMapper.repository(:default).adapter.execute(sql_statement)
end
end
At least, that's how I read the dm-transactions spec about rollbacks:
it 'should rollback when an error is raised in a transaction' do
@user_model.all.size.should == 0
lambda {
@user_model.transaction do
@user_model.create(:name => 'carllerche')
raise 'I love coffee'
end
}.should raise_error('I love coffee')
@user_model.all.size.should == 0
end
I've been writing a sizable application using DataMapper with plenty of transactions and without the use of rollback()
, and all of my failed transactions always rollback.
Also, from what I remember about ActiveRecord (it's been a year since I've used AR), the DataMapper transaction behavior mimics the AR behavior.
Im principle, the response is still correct. User.transaction will open a transaction on the repository (database) that the model "User" is attached to. A more generic way is
DataMapper.repository(:default).transaction do |t|
t.commit
end