- 首页 |
- linux |
- ruby |
- ROR |
- 敏捷开发 |
- 扯 |
- 桌面show |
- Ajax on rails |
- design pattern |
- The Ruby Way |
- railscast |
- 技巧 |
- mac |
- mysql |
- 粒计算 |
- 数工 |
- flex on rails
热门文章
搜索BLOG文章
最新评论
- [匿名]admin:什么玩艺?
- [匿名]lynn1221:每一种数据库系统都有优点和缺点,..
- [匿名]sfsa:垃圾检测
- [匿名]aoner:这问题2.3.8都还没解决,用了你的方..
- fsjoy1983:废话,你在.git外部一层目录执行能..
51CTO推荐博文
更多>> rails实现“事务”的方法
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://fsjoy.blog.51cto.com/318484/96738
在学习数据库时,曾经提到事务,最典型的一个实例就是银行转账的问题。
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, 数据迁移:
在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
-------
让事务自动恢复对象状态也有一个缺点:你将无法获知验证过程中出现的错误信息,非法的对象不会被保存,事务会将所有修改回滚,但没有什么简单方法可以知道究竟哪里出了错。
本文出自 “李骥平” 博客,请务必保留此出处http://fsjoy.blog.51cto.com/318484/96738
相关文章
2008-09-02 12:54:57
来看看。支持一下!
2009-10-30 15:48:55
如果A减了100,正好这时银行数据库断电了,程序没有来得及回滚,怎么办,虽然这种情况跟中大奖差不多
2009-11-12 17:03:26
银行有对账系统,每天会对所有交易帐目进行比对,即使程序没来得及回滚,也会在对账过程中发现异常,并回溯到原先状态