zoukankan      html  css  js  c++  java
  • RoR transaction

    首先看 transaction 方法

    1. ActiveRecord::Transactions 
      Public Class methods  
    2. # File vendor/rails/acti
      verecord/lib/active_record/
      transactions.rb, line 187  
    3. 187: def transaction(&block)  
    4. 188: self.class.transaction(&block)  
    5. 189: end 

    可以看出他是调用类方法,类方法代码如下:

    1. # File vendor/rails/activerecord/lib/
      active_record/transactions.rb, line 75  
    2. def transaction(&block)  
    3. increment_open_transactions  
    4. begin  
    5. #connection is kind of MysqlAdapter  
    6. connection.transaction(Thread.current
      ['start_db_transaction'], &block)  
    7. ensure  
    8. decrement_open_transactions  
    9. end  
    10. end  
    11. #connection.transaction call method in  
    12. # File vendor/rails/activerecord/
      lib/active_record/connection_
      adapters/abstract/database_
      statements.rb, line 58  
    13. # Wrap a block in a transaction. 
      Returns result of block.  
    14. def transaction(start_db_transaction = true)  
    15. transaction_open = false 
    16. begin  
    17. if block_given?  
    18. if start_db_transaction  
    19. begin_db_transaction  
    20. transaction_open = true 
    21. end  
    22. yield  
    23. end  
    24. rescue Exception =>
       database_transaction_rollback  
    25. if transaction_open  
    26. transaction_open = false 
    27. rollback_db_transaction  
    28. end  
    29. raise unless database_transaction
      _rollback.is_a? ActiveRecord::Rollback  
    30. end  
    31. ensure  
    32. if transaction_open  
    33. begin  
    34. commit_db_transaction  
    35. rescue Exception => database
      _transaction_rollback  
    36. rollback_db_transaction  
    37. raise  
    38. end  
    39. end  
    40. end 

    上面的代码即实现了rails中的transaction,可见ActiveRecord是不支持Ruby on Rails事物嵌套的。 如果模型使用的是相同的数据库, 那么用 ModelA.transaction 或 ModelB.transaction的作用是一样的。

    1. Code  
    2. objecta.transaction do  
    3. objectb.save!  
    4. end 

    或者

    1. objectb.transaction do  
    2. objecta.save!  
    3. end 

    或者

    1. ModelA.transaction do  
    2. objectb.save!  
    3. end 

    都是一样的!

    这些对象的方面或类方面, 到最后都是转换成SQL,让数据库来执行, 如果明白这个,一切都变得简单了!

    就从SQL而言 "model.transaction do" 只是执行 Begin, "end" 执行Commit. 对于MYSQL个别是引挚支持的存储点功能不在本文讨论范围之内。补充一下, 目前只是SQLServer支持Ruby on Rails事物嵌套,所以如果说ROR支持事务嵌套也就有点勉强!

    ActiveRecord事务


    一、介绍:

    在你使用Transcations API时,系统就是像在基础数据库内使用SQL BEGIN...COMMIT。

    注意:基础数据库必须支持事务。对于MySQL这意味着使用InnoDB存储引擎。

    每个“活动记录”对象都有一个transaction方法,它接受一个块。在BEGIN...COMMIT上下文环境内的数据库动作都在块内发生。通常在块结束时,修改被提交。如果块内引起了一个异常,所有修改会被回滚,事务中止。

    数据库事务将一系列修改组合在一起,以一种方式要么完成所有修改,要么一个都不修改。

    二、对象级事务:

    你可以为“活动记录”对象打开对象级事务。通过将你想使用对象级事务的每个“活动记录”的名字传递给transaction()方法。像这样:

    Account.transaction(david, mary) do

    david.withdrawal(100)

    mary.deposit(100)

    end

    如果事务失败,David和Mary将被返回到它的原有状态。在数据库和这两个对象内的金额均不会被修改。

    也就是说,transaction也可以接受“模型”对象做为一个参数,在此情况下,“模型”将被回滚到它们的原有(事务前)状态,事务被强行中止。

    记住,“活动记录”不能主动跟踪不同对象的更新前后的状态--事实上它也不可能,因为没有简单的办法知道哪个model是在事务处理当中。这就会出现这种情况:在确认失败时,数据库没有被修改,但对象却被修改了。通过使用对象级事务就可以避免这些。

    在稍后的例子中,你会看到对象级事务的用法。

    三、Save 和 destroy 被自动地包装在一个事务中:

    Base#save 和 Base#destroy 被包装在事务中,以确保在任何时候可以确认或回滚。所以你可以使用确认来检查事务的值或者你引发一个异常来回调回滚。

    在存储或删除行时“活动记录”使用同一事务技术。

    四、异常处理:

    也要小心在事务块中抛出的异常将会被传播(在触发回滚之后),所以你应该在你的代码中准备捕获这些异常。

    例如:

    peter = Account.create(:balance => 100, :number => "12345")

    paul = Account.create(:balance => 200, :number => "54321")

    begin

    Account.transaction(peter, paul) do

    paul.deposit(350)

    peter.withdraw(350)

    end

    rescue

    puts "Transfer aborted"

    end

    puts "Paul has #{paul.balance}"

    puts "Peter has #{peter.balance}"

    五、一个例子:

    我们看一个在《Agile Web Development with Rails 》一书中的一个完整的例子。

    1、数据库:

    create table accounts (

    id int not null auto_increment,

    number varchar(10) not null,

    balance decimal(10,2) default 0.0,

    primary key (id)

    ) type=InnoDB;

    2、模型:

    class Account < ActiveRecord::Base

    def withdraw(amount)

    adjust_balance_and_save(-amount)

    end

    def deposit(amount)

    adjust_balance_and_save(amount)

    end

    #对象级事务

    def self.transfer(from, to, amount)

    transaction(from, to) do

    from.withdraw(amount)

    to.deposit(amount)

    end

    end

    private

    def adjust_balance_and_save(amount)

    self.balance += amount

    save! #确认失败时引发异常

    end

    #确认:余额不能小于0元。

    def validate

    errors.add(:balance, "is negative") if balance < 0

    end

    end

    六、应用:

    peter = Account.create(:balance => 100, :number => "12345")

    paul = Account.create(:balance => 200, :number => "54321")

    #进行事务处理,并捕获确认失败。

    Account.transfer(peter, paul, 350) rescue puts "Transfer aborted"

    puts "Paul has #{paul.balance}"

    puts "Peter has #{peter.balance}"

    输出:

    Transfer aborted

    Paul has 200.0

    Peter has 100.0

    rails批量更新

    ruby 代码  user_controller.rb

    1. begin
    2. User.transaction do
    3.    User.update(params[:user].keys,params[:user].values)   
    4. end
    5. end

    rails 提供的批量更新update方法,返回一个对象数组,并不好判断保存数据是否执行成功,以下代码可以判断

    ruby 代码

    1. begin
    2.   User.transaction do
    3.     params[:user].each do |id, value|      
    4.       user= User.find(id)      
    5.       user.update_attributes!(value)      
    6. end
    7. end
    8.   flash[:notice] = "保存数据成功"
    9. rescue
    10.   flash[:notice] = "保存数据失败"
    11. end

    xml 代码

    这是 user.rhtm

    1. <% for @user in @users%>
    2. <td><%= text_field("user[]","name",'size'=>'10')%>td>
    3. <td><%= password_field("user[]","password",'size'=>'10')%>td>
    4. <td>
    5. <%=   
    6.     select("user[]","city_id",@cities.collect{|p|[p.city,p.id]},options = {:include_blank => true})   
    7.    %></td>
    8. <% end %>
  • 相关阅读:
    青山一别
    Spring Mvc Long类型精度丢失
    Spring 版MediatR--中介者模式实现库
    Spring 实现策略模式--自定义注解方式解耦if...else
    spring泛型注入
    Spring Boot Mvc 统一返回结果
    简化mapstruct代码: mapstruct-spring-plus
    .NET后端开发编码规范
    深度优先遍历(DFS)和广度优先遍历(BFS)的实现与简单应用
    将HTML字符串编译为虚拟DOM对象的基础实现
  • 原文地址:https://www.cnblogs.com/mingforyou/p/2705647.html
Copyright © 2011-2022 走看看