在学习数据库时,曾经提到事务,最典型的一个实例就是银行转账的问题。
A帐户向B帐户转账100块,这个事情一定要发生两件事情,A的帐户上减去100块,B的账户上加上100块。如果半路发生问题而中断这个事情,那么必须回滚到初始状态。
下面来看一下用rails来实现这个例子:
首先,建立模型类account, 数据迁移:
在account类中定义方法:
实施数据迁移。运行console
>> peter = Account.create(:balance=>100, :number=>"12345")
>> paul = Account.create(:balance=>200, :number=>"54321")
#create直接创建并保存到accounts表中
现在数据库中的内容#SELECT * FROM account_development.account
继续在console里运行:
>> Account.transaction do
?> paul.deposit(10)
>> peter.withdraw(10)
>> end
此时数据库中的内容:
现在再来看看异常情况,1.从peter帐户上转350出去给peter
>> Account.transaction do
?> paul.deposit(350)
>> peter.withdraw(350)
>> end
------
这时抛出了异常:
ActiveRecord::RecordInvalid: Validation failed: Balance is negative!
---数据库还是保持原样:
2.从peter账户上转10出去给tom
>> Account.transaction do
?> paul.deposit(10)
>> tom.withdraw(10)
>> end
----抛出异常:
NameError: undefined local variable or method `tom' for #<Object:0x389a0>
数据库依然保持原样。
虽然数据库依然保持原样,但是模型对象会发生变化,look:
现在还是恢复原状,peter有100块,paul有200块
现在执行:
结果出乎意料,
Transfer aborted!
Paul has 550.0
peter has -250.0
模型对象已经被改变了!
原因就在于ActiveRecord并没有跟踪对象在事务前后的状态,实际上它也跟踪不了, 因为没有一种简单的办法可以知道哪些模型对象参与了事务。为了解决这个问题,我们可以把涉及一次事务的模型对象以参数的形式明确的告诉transaction
这次结果就跟我们料想的一样了:
Transfer aborted!
Paul has 200.0
peter has 100.0
现在可以将转账的方法写到Account类中去了,一次转账涉及两个帐户, 并且不是由其中的任何一个来发起的, 所以这个方法时一个类方法, 接受两个account对象作为参数:
试试在console下使用这个方法:
>> peter=Account.find(1)
>> paul=Account.find(2)
>> Account.transfer(peter, paul, 350) rescue puts "Transfer aborted!"
#结果:
#Transfer aborted!
=> nil
------
>> puts "Paul has #{paul.balance}"
#结果
Paul has 200.0
=> nil
------
>> puts "Peter has #{peter.balance}"
#结果:
Peter has 100.0
=> nil
-------
让事务自动恢复对象状态也有一个缺点:你将无法获知验证过程中出现的错误信息,非法的对象不会被保存,事务会将所有修改回滚,但没有什么简单方法可以知道究竟哪里出了错。
A帐户向B帐户转账100块,这个事情一定要发生两件事情,A的帐户上减去100块,B的账户上加上100块。如果半路发生问题而中断这个事情,那么必须回滚到初始状态。
下面来看一下用rails来实现这个例子:
首先,建立模型类account, 数据迁移:
![](http://blog.51cto.com/attachment/200809/200809021220358873482.png)
在account类中定义方法:
![](http://blog.51cto.com/attachment/200809/200809021220359419546.png)
实施数据迁移。运行console
![](http://blog.51cto.com/attachment/200809/200809021220359096207.png)
>> peter = Account.create(:balance=>100, :number=>"12345")
>> paul = Account.create(:balance=>200, :number=>"54321")
#create直接创建并保存到accounts表中
现在数据库中的内容#SELECT * FROM account_development.account
![](http://blog.51cto.com/attachment/200809/200809021220359268021.png)
继续在console里运行:
>> Account.transaction do
?> paul.deposit(10)
>> peter.withdraw(10)
>> end
此时数据库中的内容:
![](http://blog.51cto.com/attachment/200809/200809021220359652105.png)
现在再来看看异常情况,1.从peter帐户上转350出去给peter
>> Account.transaction do
?> paul.deposit(350)
>> peter.withdraw(350)
>> end
------
这时抛出了异常:
ActiveRecord::RecordInvalid: Validation failed: Balance is negative!
---数据库还是保持原样:
![](http://blog.51cto.com/fsjoy/attachment/200809/200809021220359652105.png)
2.从peter账户上转10出去给tom
>> Account.transaction do
?> paul.deposit(10)
>> tom.withdraw(10)
>> end
----抛出异常:
NameError: undefined local variable or method `tom' for #<Object:0x389a0>
数据库依然保持原样。
虽然数据库依然保持原样,但是模型对象会发生变化,look:
现在还是恢复原状,peter有100块,paul有200块
![](http://blog.51cto.com/fsjoy/attachment/200809/200809021220359268021.png)
现在执行:
![](http://blog.51cto.com/attachment/200809/200809021220361184858.png)
结果出乎意料,
Transfer aborted!
Paul has 550.0
peter has -250.0
模型对象已经被改变了!
原因就在于ActiveRecord并没有跟踪对象在事务前后的状态,实际上它也跟踪不了, 因为没有一种简单的办法可以知道哪些模型对象参与了事务。为了解决这个问题,我们可以把涉及一次事务的模型对象以参数的形式明确的告诉transaction
![](http://blog.51cto.com/fsjoy/attachment/200809/200809021220361152170.png)
这次结果就跟我们料想的一样了:
Transfer aborted!
Paul has 200.0
peter has 100.0
现在可以将转账的方法写到Account类中去了,一次转账涉及两个帐户, 并且不是由其中的任何一个来发起的, 所以这个方法时一个类方法, 接受两个account对象作为参数:
![](http://blog.51cto.com/attachment/200809/200809021220361425442.png)
试试在console下使用这个方法:
>> peter=Account.find(1)
>> paul=Account.find(2)
>> Account.transfer(peter, paul, 350) rescue puts "Transfer aborted!"
#结果:
#Transfer aborted!
=> nil
------
>> puts "Paul has #{paul.balance}"
#结果
Paul has 200.0
=> nil
------
>> puts "Peter has #{peter.balance}"
#结果:
Peter has 100.0
=> nil
-------
让事务自动恢复对象状态也有一个缺点:你将无法获知验证过程中出现的错误信息,非法的对象不会被保存,事务会将所有修改回滚,但没有什么简单方法可以知道究竟哪里出了错。
本文转自 fsjoy1983 51CTO博客,原文链接:http://blog.51cto.com/fsjoy/96738,如需转载请自行联系原作者