Rails开发细节《二》CRUD

Rails数据库操作指南
本文介绍Rails环境下数据库操作方法,包括创建、保存、读取、更新和删除等基本操作,同时还涉及SQL查询、聚合统计及范围查询等内容。

Create & Save

创建 & 保存


  1. order = Order.new 
  2. order.name = "virusswb" 
  3. order.email = "asdf@exmaple.com" 
  4. order.save 

调用save方法之后就可以保存到数据库了。

还可以用block来创建并保存。


  1. Order.new do |order| 
  2.   order.name = "virusswb" 
  3.   order.email = "asdf@example.com" 
  4.   order.save 
  5. end 

还可以使用hash的方式创建并保存。

 


  1. order = Order.new
  2.   :name => "virus"
  3.   :email => "adf@example.com" 
  4. order.save 

默认id主键不用赋值,是integer的自增列。

上面的方法都需要调用save方法才能保存到数据库,用create方法可以合二为一,创建同时保存到数据库。

 


  1. order = Order.create( 
  2.   :name => "virus"
  3.   :email => "adf@exmaple.com" 

还可以一次保存多个,只要给create传递一个数组就可以实现。

 


  1. orders = Order.create( 
  2.   [{ :name => "virus1", 
  3.      :email => "111@123.com" 
  4.    }, 
  5.    { :name => "swb2", 
  6.      :email => "222@123.com" 
  7.    }] 

返回也是一个数组。

从form放入参数中直接创建一个model对象。

 


  1. @order = Order.new(params[:order]) 

Order.find(1)可以用来获取id=1的model,但是如果数据表中不存在id=1的model,就会抛出RecordNotFound异常。

如果使用Order.where(:id => 1).first,是告诉数据库“我需要id=1的记录”,如果不存在这样的记录,不会抛出异常,返回的是nil。

Reading Existing Rows

读取

 


  1. an_order = Order.find(27) 

返回一条记录,如果没有,会抛出RecordNotFound异常。

 


  1. product_list = params[:product_ids] 
  2. total = Product.find(product_list).sum(&:price) 

给find传入array,可以实现多条记录的查询,但是如果其中一个id不存在的话,还是会抛出RecordNotFound异常 。

Dynamic Finders

动态查询

有时候我们会针对model的一列进行查询,在rails中不用写sql就可以实现。

 


  1. order = Order.find_by_name("virus"
  2. orders = Order.find_all_by_name("virus"
  3. orders = Order.find_all_by_email(params['email']) 

 


  1. order = Order.find_by_name("virus"


  1. order = Order.where(:name => "virus").first 

效果是一样的。

还有find_all_by_xxx和find_last_by_xxx这样的查询。

如果在find_by_方法后面加上一个!,就会在找不到记录的情况下抛出RecordNotFound异常。

 


  1. order = Order.find_by_name!("virus"

还可以多列查询。

 


  1. order = Order.find_by_name_and_password(name,pw) 


  1. order = Order.where(:name => name, :password => pw).first 

是一样的效果。

有些情况下,你想要确保有model,如果在数据库中不存在,就创建一个,动态的finders就可以实现这个需求。find_or_initialize_by_或者find_or_create_by_可以实现没有满足条件的记录,就返回nil,就会创建或者直接保存记录。

 


  1. cart = Cart.find_or_initialize_by_user_id(user.id) 
  2. cart.items << new_item 
  3. cart.save 

SQL and ActiveRecord

在AcriveRecord中使用SQL

用model的where方法可以实现sql语句的where子句。


  1. pos = Order.where("name = 'virus' and emal = 'sf@123.com'"

 

接收外部传过来的name的值。


  1. name = params[:name] 
  2. pos = Order.where("name = '#{name}' and email = '123@123.com'") 

上面拼接sql语句的方式,会受到sql注入攻击。

安全的方式是让ActiveRecord来处理,参数化。

 


  1. name = params[:name
  2. pos = Order.where(["name = ? and email ='sd'", name]) 

 


  1. name = params[:name
  2. email = params[:email
  3. pos = Order.where("name = :name and email = :email",  
  4.                 { :name => name, :email => email}) 

 

因为params已经是hash了,可以简洁一些。

 


  1.  
  2. pos = Order.where("name = :name and email = :email",  
  3.                 params[:order]

还可以更简洁一些。

 


  1. pos = Order.where(params[:order]

要小心上面的写法,它使用了所有的参数,通过下面的写法,你可以指定使用哪些参数。

 


  1. pos = Order.where( :name => params[:name], :email => params[:email]) 

Using like Clauses

使用like子句

 


  1. User.where("name like ?", params[:name] + "%"

Subsetting the Recods Returned

返回部分数据

order排序

 


  1. orders = Order.where(:name => params[:name]) 
  2.               .order("name, email desc"

limit限制返回的条数

 



  1. orders = Order.where(:name => params[:name]) 
  2.               .order("name, email desc")
  3. .limit(10)

offset跳过行

offset相当于mysql中的skip。


  1. def Order.find_on_page(page_num, page_size)
  2. orders = Order.where(:name => params[:name]) 
  3.                .order("name, email desc")
  4. .limit(page_size)
  5. .offset(page_num*page_size)
  6. end

select选择列

默认选择所有的列,可以通过select来查询需要的列。


  1. orders = Order.where(:name => params[:name]) 
  2.               .order("name, email desc")
  3. .select("name, email")

joins连接



  1. LineItem.select('li.quantity'
  2. .where("pr.title = 'programming in ruby'"
  3. .joins("as li inner join products pr on li.product_id=pr.id"

readonly只读

使用readonly方法获取的记录,不能再保存回数据库。

如果使用joins或者是select方法,返回的对象自动标记为readonly。

group分组

会产生group by子句。


  1. summary = LineItem.select(select = > "sku, sum(amount) as amount"
  2. .group("sku"

lock锁

lock方法的参数是一个字符串,告诉数据库使用指定类型的锁。

在mysql中共享锁返回最新的数据行,并且确保没有人可以在锁期间修改这条记录。


  1. Account.transaction do  
  2.   ac = Account.where(:id => id).lock("LOCK IN SHARE MODE").first 
  3.   ac.balance -= amount if ac.balance > amount 
  4.   ac.save 
  5. end 

 

Getting Column Statistics

聚合统计


  1. average = Order.average(:amount
  2. max = Order.maximun(:amount
  3. min = Order.minimun(:amount
  4. total = Order.sum(:amount
  5. number = Order.count 

上面的这些聚合函数都是数据库无关的。

也可以联合其他方法一起使用。

 


  1. Order.where("amount > 20").minimun(:amount)

通常情况,这些聚合函数返回的是一个值。如果包含group方法,返回的就是每一组的聚合结果,也就是返回一系列值。

 


  1. result = Order.maximun(:amount).group(:state
  2. puts result #=> [["TX", 123456], ["NX", 3456]] 

Scopes

范围查询


  1. class Order < ActiveRecord::Base 
  2.   scope :last_n_days, lambda do |days| 
  3.     where("updated < ?", days) 
  4.   end 
  5. end 
  6.  
  7. orders= Order.last_n_days(7) 
  8.  
  9. class Order < ActiveRecord::Base 
  10.   scope :checks, where(:pay_type => :check
  11. end 
  12.  
  13. orders = Order.checks.last_n_days(7) 
  14.  
  15. in_house = Order.where('email like "%@pragprog.com"'
  16.  
  17. in_house.checks.last_n_days(7) 

Writing Our Own SQL

执行查询SQL


  1. orders = LineItem.find_by_sql("select line_items.* from line_items, orders where order_id = orders.id and orders.name = 'virus'" 

 

 


  1. orders = Order.find_by_sql("select name, pay_type from orders"
  2.  
  3. first = orders[0] 
  4. p first.attributes  #=>{"name" => "shi", "pay_type" => "check"} 
  5. p first.attribute_names #=>["name", "pay_type"] 
  6. p first.attribute_present?("address"#=>false 

Reloading Data 

重新加载数据

我们的应用会被多个用户访问,有的时候,之前取出来的数据已经被其他用户修改,这时候你又需要最新的数据,可以调用model的reload方法来实现。

 


  1. loop do 
  2.   puts "Price = #{stock.price}" 
  3.   sleep 60 
  4.  
  5.   stock.reload 
  6. end 

Updating Existing Rows

更新数据

调用model的save方法就可以实现更新,通过id来匹配数据,如果存在数据,就是更新,如果不存在数据,就插入一条。


  1. order = Order.find(10) 
  2. order.name = "swb" 
  3. order.save 
  4.  
  5. orders = Order.find_by_sql("select id, name, email from orders where id = 12"
  6. first = orders[0] 
  7. first.name = "newname" 
  8. first.save 
  9.  
  10. order = Order.find(12) 
  11. order.update_attribute(:name"newname"
  12.  
  13. order = Order.find(12) 
  14. order.update_attributes(:name => "newname":email => "newmail"
  15.  
  16. def save_after_edit 
  17.   order = Order.find(params[:id]) 
  18.   if order.update_attributes(params[:order]) 
  19.     redirect_to :action => :index 
  20.   else 
  21.     render :action => :edit 
  22.   end 
  23. end 
  24.  
  25. order = Order.update(12, :name => "newname":email => "newmail"
  26.  
  27. result = Order.update_all("price = 1.1*price""title like '%java%'"

 

save, save!, create, create!

  • save,保存成功返回true,否则返回nil。
  • save!,保存成功返回true,否则抛出异常。
  • create,无论保存成功与否,都会返回model对象。你需要通过检查validation errros来确定保存是否成功。
  • create!,保存成功返回true,否则抛出异常。

  1. if order.save 
  2.   # all ok 
  3. else 
  4.   # validation fail 
  5. end 
  6.  
  7. begin 
  8.   order.save! 
  9. rescue RecordInvalid => error 
  10.   # validation fail 
  11. end 

Deleting Rows

删除数据

类级别的delete方法

 


  1. Order.delete(10) 
  2. User.delete([2,3,4,5]) 
  3. Product.delete_all(["price > ?"@expensize_price]) 

delete方法通过id或者id集合删除数据,delete_all通过条件删除数据,返回值是受影响的行数,没有符合条件的数据也不会抛出异常。

对象级别的destroy方法

 


  1. order = Order.find(10) 
  2. order.destroy 

调用对象的destroy方法之后,就会锁定对象,防止对他进行修改。

 

Order.destroy_all(["price > ?"@expensize_price])

delete方法会忽略验证功能,destroy方法会确保它们被执行。通常来说,如果你想要确保你的数据符合业务规则,最好使用destroy方法。




本文转自 virusswb 51CTO博客,原文链接:http://blog.51cto.com/virusswb/1016292,如需转载请自行联系原作者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值