这是前段时间有人讨论过的问题:
假定orders表有10个字段,你只想更新其中一个,但active record会生成一个更新全部字段的SQL语句,假定其中一个字段值长度是20K,这个负担可能会有些重。
我尝试解决这个问题,写了个简单的插件:
代码: |
order = Order.find(1) order.update_attribute(:status, 'finished') |
假定orders表有10个字段,你只想更新其中一个,但active record会生成一个更新全部字段的SQL语句,假定其中一个字段值长度是20K,这个负担可能会有些重。
我尝试解决这个问题,写了个简单的插件:
代码: |
module ActiveRecord class Base def update_attribute(name, value) update_attributes(name => value) end def update_attributes(new_attributes) return if new_attributes.nil? attributes = new_attributes.dup attributes.stringify_keys! self.attributes = attributes update(attributes) end private def update(attrs = nil) connection.update( "UPDATE #{self.class.table_name} " + "SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false, attrs))} " + "WHERE #{self.class.primary_key} = #{quote(id)}", "#{self.class.name} Update" ) return true end def attributes_with_quotes(include_primary_key = true, attrs = nil) (attrs || attributes).inject({}) do |quoted, (name, value)| if column = column_for_attribute(name) quoted[name] = quote(value, column) unless !include_primary_key && column.primary end quoted end end end end |
attributes_with_quotes函数的参数搞这么复杂,原因是我想即便是用这段代码替换库里面的部分,也不影响原有代码的正常功能。
可以简单测试一下上面的例子,它生成的SQL语句会简洁很多,大概是这样子:
UPDATE orders SET "status" = 'finished' WHERE id = 1
已发现的BUG和修复:
1、没有调用validation (by cookoo)。由于原有代码调用save,而save被覆盖成有验证的代码,所以具有验证功能。解决办法是增加一段代码:
module ActiveRecord
module ValidationsFix
def self.append_features(base) # :nodoc:
super
base.class_eval do
alias_method :update_attributes_without_validation, :update_attributes
alias_method :update_attributes, :update_attributes_with_validation
end
end
def update_attributes_with_validation(new_attributes)
return if new_attributes.nil?
attributes = new_attributes.dup
attributes.stringify_keys!
self.attributes = attributes
if valid?
update_attributes_without_validation(attributes)
else
return false
end
end
end
end
ActiveRecord::Base.class_eval do
include ActiveRecord::ValidationsFix
end
module ValidationsFix
def self.append_features(base) # :nodoc:
super
base.class_eval do
alias_method :update_attributes_without_validation, :update_attributes
alias_method :update_attributes, :update_attributes_with_validation
end
end
def update_attributes_with_validation(new_attributes)
return if new_attributes.nil?
attributes = new_attributes.dup
attributes.stringify_keys!
self.attributes = attributes
if valid?
update_attributes_without_validation(attributes)
else
return false
end
end
end
end
ActiveRecord::Base.class_eval do
include ActiveRecord::ValidationsFix
end
简单测试通过。