rails 对数据库的一些操作:新增,查询等

基础操作

如何新增

ActiveRecord提供了四种API,分别是save、save!、create和create!:

a = Category.new( :name => 'Ruby', :position => 1 )
a.save

b = Category.new( :name => 'Perl', :position => 2 )
b.save!
        
Category.create( :name => 'Python', :position => 3 )
c = Category.create!( :name => 'PHP', :position => 4 )

其中createcreate!就等于new完就savesave!,有无惊叹号的差别在于validate资料验证不正确的动作,无惊叹号版本会回传布林值(true或false),有惊叹号版本则是验证错误会丢出例外。

何时使用惊叹号版本呢?save和create通常用在会处理回传布林值(true/false)的情况下(例如在 controller 里面根据成功失败决定 render 或 redirect),否则在预期应该会储存成功的情况下,请用 save!或create! 来处理,这样一旦碰到储存失败的情形,才好追踪 bug。

透过 :validate => false 可以略过验证

c.save( :validate => false )

在 Rails3 之前的版本是 user.save(false)

如何查询

ActiveRecord 使用了 Arel 技术来实作查询功能,你可以自由组合 where、limit、select、order 等条件。

Arel 是 relational algebra” library。但根据 2.0 实作者 tenderlove 的说法,也可以说是一种 SQL compiler。 http://engineering.attinteractive.com/2010/12/architecture-of-arel-2-0/

first, last 和 all

这三个方法可以分别拿出资料库中的第一笔、最后一笔及全部的资料:

c1 = Category.first
c2 = Category.last
categories = Category.all # 这会是一个阵列

如果资料量较多,请不要在正式上线环境中执行.all 把所有资料拿出来,这样会耗费非常多的记忆体。请用分页或缩小查询范围。

find

已知资料的主键 ID 的值的话,可以使用 find 方法:

c3 = Category.find(1)
c4 = Category.find(2)

find 也可以接受阵列参数,这样就会一次找寻多个并回传阵列:

arr = Category.find([1,2])
# 或是
arr = Category.find(1,2)

如果找不到资料的话,会丢 ActiveRecord::RecordNotFound 例外。如果是 find_by_id 就不会丢出例外,而是回传 nil。

find_by_* 和 find_all_by_*

find_by_* 和 find_all_by_* 是 Rails 的动态方法,可以自由用 and 组合,例如:

c5 = Category.find_by_name('Ruby')
c6 = Category.find_by_name_and_position('Ruby', 1)
c7 = Category.find_all_by_position(2)
find_by_sql

如果需要手动撰写 SQL,可以使用 find_by_sql,例如:

c8 = Category.find_by_sql("select * from categories")

不过在绝大多数的情况,是不需要手动写 SQL 的。

where 查询条件

where 可以非常弹性的组合出 SQL 查询,例如:

c9 = Category.where( :name => 'Ruby', :position => 1 )
c10 = Category.where( [ "name = ? or position = ?", 'Ruby', 2] )

其中参数有两种写法,一种是 Hash,另一种是 Array。前者的写法虽然比较简洁,但是就没办法写出 or 的查询。注意到不要使用字串写法,例如

Category.where("name = #{params[:name]}") # 请不要这样写

这是因为字串写法会有 SQL injection 的安全性问题,请改用阵列写法。

另外,where 是 lazy loading,也就是直到真的需要取值的时候,才会跟资料库拿资料。如果需要立即触发,可以接著使用 .all, .first, .last,例如

c11 = Category.where( :name => 'Ruby', :position => 1 ).all

limit

limit 可以限制笔数

c = Category.limit(5).all
c.size # 5

order

order 可以设定排序条件

Category.order("position")
Category.order("position DESC")
Category.order("position DESC, name ASC")

如果要消去order条件,可以用reorder

Category.order("position").reorder("name") # 改用 name 排序
Category.order("position").reorder(nil) # 取消所有排序

offset

offset 可以设定忽略前几笔不取出,通常用于资料分页:

c = Category.limit(2)
c.first.id # 1
Category.limit(2).offset(3)
c.first.id # 4

select

预设的 SQL 查询会取出资料的所有栏位,有时候你可能不需要所有资料,为了效能我们可以只取出其中特定栏位:

Category.select("id, name")

例如栏位中有 Binary 资料时,你不会希望每次都读取出庞大的 Binary 资料占用记忆体,而只希望在使用者要下载的时候才读取出来。

readonly

c = Category.readonly.first

如此查询出来的c就无法修改或删除,不然会丢出ActiveRecord::ReadOnlyRecord例外。

group 和 having

(TODO)

串接写法

以上的 where, order , limit, offset, joins, select 等等,都可以自由串接起来组合出最终的 SQL 条件:

c12 = Category.where( :name => 'Ruby' ).order("id desc").limit(3)

find_each 批次处理

如果资料量很大,但是又需要全部拿出来处理,可以使用 find_each 批次处理

Category.where("position > 1").find_each do |category|
    category.do_some_thing
end

预设会批次捞 1000 笔,如果需要设定可以加上 :batch_size 参数。

重新载入

如果已经读取的 AR 资料,需要重新载入,可以用 reload 方法:

p = Category.first
p.reload

如何删除

一种是先抓到该物件,然后删除:

c12 = Category.first
c12.destroy

另一种是直接对类别呼叫删除,传入 ID 或条件:

Category.delete(2)
Category.delete_all(conditions = nil)
Category.destroy_all(conditions = nil) 

delete 不会有 callback 回呼,destroy 有 callback 回呼。什么是回呼详见下一章。

统计方法

Category.count
Category.average(:position)
Category.maximum(:position)
Category.sum(:position)

其中我们可以利用上述的 where 条件缩小范围,例如:

Category.where( :name => "Ruby").count

如何更新

c13 = Category.first
c13.update_attributes(attributes)
c13.update_attributes!(attributes)
c13.update_attribute(attribute_name, value)

注意 update_attribute 会略过 validation 资料验证 注意 mass assign 安全性问题,可以透过 attr_protected 或 attr_accessor 设定。

发布了23 篇原创文章 · 获赞 7 · 访问量 12万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章