Ruby on rails 3 Rails 3-事务和锁定

Ruby on rails 3 Rails 3-事务和锁定,ruby-on-rails-3,transactions,locking,Ruby On Rails 3,Transactions,Locking,我是Rails新手,有一个需要处理事务的系统。用户可以输入与一个或多个用户绑定的事务。这些用户欠进行交易的人一些钱。例如,比尔可能为4个朋友买午餐,账单是125美元。他们决定将账单分成5份,每人欠25美元。比尔将总共输入125美元,并将每个朋友(包括他自己)输入为欠交易的25美元我的控制器和模型中都有代码来实现这个目标,但我不知道是否正确使用了事务和锁定。此外,这是将此信息保存在控制器中的一种非预期方式吗?我正在使用事务,因为所有这些操作必须同时发生或失败(原子性),并且我需要锁定,以防多个用户

我是Rails新手,有一个需要处理事务的系统。用户可以输入与一个或多个用户绑定的事务。这些用户欠进行交易的人一些钱。例如,比尔可能为4个朋友买午餐,账单是125美元。他们决定将账单分成5份,每人欠25美元。比尔将总共输入125美元,并将每个朋友(包括他自己)输入为欠交易的25美元我的控制器和模型中都有代码来实现这个目标,但我不知道是否正确使用了事务和锁定。此外,这是将此信息保存在控制器中的一种非预期方式吗?我正在使用事务,因为所有这些操作必须同时发生或失败(原子性),并且我需要锁定,以防多个用户同时提交(隔离)。也许我应该让后端的db处理锁定?它已经做到了吗?比如说MySQL?谢谢

trans_controller.rb
class TransController
tran.rb

class Tran < ActiveRecord::Base
    has_many :transaction_users, :dependent => :destroy, :class_name => 'TransactionUser'
    belongs_to :submitting_user, :class_name => 'User'
    belongs_to :buying_user, :class_name => 'User'
    
    accepts_nested_attributes_for :transaction_users, :allow_destroy => true

    validates :description, :presence => true,
                            :length => {:maximum => 100 }
    #validates :total,      :presence => true
    validates_numericality_of :total, :greater_than => 0
    
    validates :submitting_user_id,      :presence => true               
    validates :buying_user_id,          :presence => true   
            
    #validates_associated :transaction_users
    
    validate :has_transaction_users?
    validate :has_correct_transaction_user_sum?
    validate :has_no_repeat_users?
    
    def update_user_balances
        # Update the buying user in the transaction
        self.buying_user.lock!
        # Update the user's total, since they were in the transction
        self.buying_user.update_attribute :current_balance, self.buying_user.current_balance - self.total
        # Add an offsetting transaction_user for this record
        buying_tran_user = TransactionUser.create!(:amount => -1 * self.total, :user_id => self.buying_user_id, :tran => self)
        #if buying_tran_user.valid?
        #   raise "Error"
        #end
        
        # Loop through each transaction user and update their balances.  Make sure to lock each record before doing the update.
        self.transaction_users.each do |tu|
            tu.user.lock!
            tu.user.update_attribute :current_balance, tu.user.current_balance + tu.amount
        end
    end
    
    def has_transaction_users?
        errors.add :base, "A transcation must have transaction users." if self.transaction_users.blank?
    end
    
    def has_correct_transaction_user_sum?
        sum_of_items = 0;
        
        self.transaction_users.inspect
        self.transaction_users.each do |key|
            sum_of_items += key.amount if !key.amount.nil?
        end
        
        if sum_of_items != self.total
            errors.add :base, "The transcation items do not sum to the total of the transaction." 
        end 
    end
     
    def has_no_repeat_users?
        user_array = []
        self.transaction_users.each do |key|
            if(user_array.include? key.user.email) 
                errors.add :base, "The participant #{key.user.full_name} has been listed more than once."
            end
        
            user_array << key.user.email
        end
    end 
end
class-Tran:destroy,:类\u名称=>'TransactionUser'
属于:提交用户:class\u name=>'user'
属于:buying用户:class\u name=>'user'
为:事务用户接受\u嵌套的\u属性,:允许\u销毁=>true
验证:description,:presence=>true,
:length=>{:max=>100}
#验证:总计,:存在=>true
验证:总计的数值性,大于等于0
验证:提交用户id,:presence=>true
验证:buying\u user\u id,:presence=>true
#验证关联的用户:事务用户
验证:是否有\u事务\u用户?
验证:交易、用户和是否正确?
验证:是否没有重复用户?
def更新用户余额
#更新交易中的购买用户
self.buying\u user.lock!
#更新用户的总数,因为他们在交易中
self.buying\u user.update\u属性:当前余额,self.buying\u user.current\u余额-self.total
#为此记录添加抵销事务处理用户
buying_tran_user=TransactionUser.create!(:amount=>-1*self.total,:user\u id=>self.buying\u user\u id,:tran=>self)
#如果购买\u tran\u user.valid?
#提出“错误”
#结束
#循环遍历每个事务用户并更新其余额。确保在执行更新之前锁定每个记录。
self.transaction_users.each do|tu|
tu.user.lock!
tu.user.update_属性:当前_余额,tu.user.current_余额+tu.amount
结束
结束
def是否有\u事务\u用户?
errors.add:base,“交易必须有交易用户。”如果self.transaction\u users.blank?
结束
def是否有正确的交易记录用户总数?
_项之和=0;
self.transaction\u users.inspect
self.transaction|user.each do|键|
项目总数+=关键金额如果!key.amount.nil?
结束
如果项目的总和!=自我总计
errors.add:base,“交易项目总和不等于交易总额。”
结束
结束
def没有重复用户吗?
用户_数组=[]
self.transaction|user.each do|键|
if(user_array.include?key.user.email)
errors.add:base,“参与者#{key.user.full_name}已列出多次。”
结束

user_array我将避免手动执行锁,因为mysql将在事务内部正确处理必要的行级锁。在这种情况下,使用事务是正确的。我要避免的是创建一个局部变量来跟踪事务是否在没有错误的情况下完成:

def create
    @title = "Create Transaction"

    # Add the transaction from the client
    @tran = Tran.new(params[:tran])

    # Update the current user
    @tran.submitting_user_id = current_user.id

    # Update the data to the database
    # This call commits the transaction and transaction users 
    # It also calls a method to update the balances of each user since that isn't
    # part of the regular commit (why isn't it?)
    begin 
        @tran.transaction do
            @tran.save! 
            @tran.update_user_balances
            trans_successful = true
        end 
    rescue
        flash.now[:error] = @tran.errors.full_messages.to_sentence          
        render 'new'
    else
        flash[:success] = 'Transaction was successfully created.'
        redirect_to trans_path
    end
end
def create
    @title = "Create Transaction"

    # Add the transaction from the client
    @tran = Tran.new(params[:tran])

    # Update the current user
    @tran.submitting_user_id = current_user.id

    # Update the data to the database
    # This call commits the transaction and transaction users 
    # It also calls a method to update the balances of each user since that isn't
    # part of the regular commit (why isn't it?)
    begin 
        @tran.transaction do
            @tran.save! 
            @tran.update_user_balances
            trans_successful = true
        end 
    rescue
        flash.now[:error] = @tran.errors.full_messages.to_sentence          
        render 'new'
    else
        flash[:success] = 'Transaction was successfully created.'
        redirect_to trans_path
    end
end