CRUD-update

我们已经讨论了很多finder 相关的方法,Active Record 关于更新记录的操作并不多。
如果你有一个Active Record 对象(或许表示为我们的orders 表的一行),你可以通过save()方法把它写入数据库,如果这个对象以前是从数据库中读出,这个存盘的操作将更新现有记录。否则save()将会插入一个新行。
如果一个现有记录行被更新,Active Record 将使用它的主键列匹配内存中的对象。包
含在Active Record 对象中的属性决定了被更新的列。即使列值没有改变,这个列也要在数据库中去更新。例如,在下面例子中,对于数据库表中定单为123 的行的所有值将被更新
order = Order.find(123)
order.name = "Fred"
order.save

但是,在下面例子中,Active Record 对象只包含属性id,name,和paytype,当对象
被保存时,只有这些列将被更新(注意,如果你想保存使用find_by_sql 获取的记录,你必
须包括id 列)。
orders = Order.find_by_sql("select id, name, pay_type from orders
where id=123")
first = orders[0]
first.name = "Wilma"
first.save

(update_attribute,update_attributes)
除了save()方法外,Active Record 还可以让你直接用单一的方法调用
update_attribute()改变属性值并保存model 对象。
order = Order.find(123)
order.update_attribute(:name, "Barney")
order = Order.find(321)
order.update_attributes(:name => "Barney", :email =>
"barney@bedrock.com")

[color=red](update,udpate_all)[/color]最后,我们使用update()和update_all()这两个类方法来组合读取和更新记录行的操
作。update()方法接受一个id 参数和一个属性集合。它获取相应的记录行,更新给定的属性,把结果保存回数据库,并返回model 对象。
order = Order.update(12, :name => "Barney", :email =>
"barney@bedrock.com")
你可以给update()传入一个id 数组和一个含有属性值的哈希表的数组,它将更新所有
相应的数据库中的记录行,并返回一个model 对象数组。

最后update_all()类方法允许你指定SQL 中update 语句里的set 和where 的子句。例
如,下面所有标题带有Java 字样产品的单价增加10%。
result = Product.update_all("price = 1.1*price", "title like
'%Java%'")
update_all()返回值取决于数据库的适配器,除了oracle,大部分数据库返回数据库中
被改变的记录行数。


[color=red]save()andsave!()[/color]有两个版本的save 方法。如果model 对象是有效的并且能够保存的话,旧版本的save()
返回true。
if order.save
# all OK
else
# validation failed
end
上面的代码是检查save 的每个调用,看看是不是你所期望的。这样做的原因是Active
Record 比较宽松,它假定save()是在controller 的action 方法的上下文中调用,而view代码将会负责把任何错误显示给用户。对大多数应用程序来说,基本上就是这样来使用的。
然而,如果你想在需要的地方保存model 对象,又想所有的错误自动得到处理的话,你
就直接使用save!()吧。这个方法如果碰到对象不能保存,就抛出一个RecordInvalid 的异常。
begin
order.save!
rescue RecordInvalid => error
# validation failed
end

[color=red]乐观锁[/color]
在一个有多进程访问同一数据库的应用程序中,可能会有这样的情况:一个进程使用的
数据正慢慢变成老数据了,而另一个进程一直想更新这个记录行。
例如,两个进程获取相应的一个特殊账号的记录行。经过几秒间隔,两个进程都要去更
新余额。每个都是以初始的记录行的内容加载一个Active Record 的model 对象。在不同的时间他们每个都使用他们各自的model 本地拷贝去更新数据库中的记录。结果就是一个“竟争条件”,最后一个更新记录行的人得到他的预期值,而第一个人的改变则丢失了。这显示

一种解决办法是锁住要更新的表或者行。为了阻止它人对它的访问和更新,使用锁来克
服并发的问题,但是这是强制性措施。它假设可能会出错于是在这种情况下就使用了锁。对
于这种原因,此途径通常被称为悲观锁。悲观锁对web 应用来说是不起作用的。因为如果你
需要确保多用户访问的一致性,而以数据库不停机的方式来管理悲观锁是非常困难的。
乐观锁并不是很直观的锁机制。相反,只在把记录行的更新写回数据库之前,它检查并
确保没有其他人仍在更改这个记录行。在rails 的实现中,每个记录行包含一个版本号。一
旦这个记录更新,版本号就会增加。当你从你的应用程序中更新时,Active Record 会检查表中这个记录行的版本号和发出更新命令的model 的版本号。如果这两个号不匹配,他就放弃更新并抛出异常。
默认情况下,乐观锁是由包含一个叫lock_version 的integer 列的表来激活的。创建一
个新记录时这个列应该被初始化为0,否则你应该让它自己来控制--Active Record 会为你打理好。
让我们看看action 内的乐观锁。我们将创建名为counters 的表,它包含一个简单的count
字段及lock_version 列。
create table counters (
id int not null auto_increment,
count int default 0,
lock_version int default 0,
primary key (id)
);
然后我们在表中创建一个记录,并把它读取到两个独立的model 对象中,然后拭着分别
更新它们。
class Counter < ActiveRecord::Base
end
Counter.delete_all
Counter.create(:count => 0)
count1 = Counter.find(:first)
count2 = Counter.find(:first)
count1.count += 3
count1.save
count2.count += 4
count2.save
当我们运行时,可以看到一个异常。rails 更新count2 失败了,因为它的值已经过时了。
/use/lib/ruby/gems/1.8/gems/activerecord-
1.9.0/lib/active_record/locking.rb:42:
in ‘update_without_timestamps':
Attempted to update a stale object (ActiveRecord::StaleObjectError)
如果你使用乐观锁,你需要在程序中捕获这些异常情况。
你也可以关闭乐观锁:
ActiveRecord::Base.lock_optimistically = false
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值