想象一下Active Record是如何處理SQL的,我們來看看find方法的:conditions參數,調用的時候像這樣:find(:all,:conditions=>…),這裡的:conditions參數決定了find方法將返回哪些記錄,它相當於Sql語句的where部分,例如,要獲取所有的名字為Dave,pay_type為po的訂單,我們這樣寫:
pos = Order.find(:all,:conditions => "name = 'dave' and pay_type = 'po'")
find方法將返回所有符合條件的記錄,並且都已經被轉換成Order類的對象,如果沒有符合條件的訂單,find方法將返回一個長度為零的數組。
如果你的條件是預定的,那麼上面的寫法就很好了,但是如果我們要指定條件中的值呢,我們這樣寫:
# get the limit amount from the form name = params[:name] # DON'T DO THIS!!! pos = Order.find(:all,:conditions => "name = '#{name}' and pay_type = 'po'")
但是,這並不是一個好主意,因為如果你的程序要發布在網絡上的話,這樣給SQL注入攻擊留下了方便(後面我們在關於安全的主題裡會討論SQL注入的話題),更好的辦法是,動態的生成SQL(注1),讓Active Record來處理它,我們可以在SQL語句中加入占位符,然後這些占位符將會在運行期被替換成指定的值,指定占位符的方法就是在SQL中使用問號,在運行時,
第一個問號將被替換為集合的第二個元素的值,以此類推,例如,我們把上面的查詢重寫一次: name = params[:name] pos = Order.find(:all,:conditions => ["name = ? and pay_type = 'po'", name])
我們也可以使用帶名字的占位符,名字前帶冒號,例如下面這樣:
name = params[:name] pay_type = params[:pay_type] pos = Order.find(:all, :conditions => ["name = :name and pay_type = :pay_type", {:pay_type => pay_type, :name => name}])
我也可以更進一步,因為params是一個hash,我們可以讓conditions部分更簡單
pos = Order.find(:all, :conditions => ["name = :name and pay_type = :pay_type", params])
不管使用哪種占位符,Active Record都會很小心地處理SQL中的引號和escape。使用動態SQL,Active Record會保護我們不受SQL注入攻擊。
注1:這裡的動態sql不同於oracle等數據庫中所指的動態sql,其含義類似於ADO.net中不拼接sql字符串,而是傳參數來防止sql注入攻擊。