资源描述
Active Record
Active Record(中文名:活动记录)是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。 Active Record 和 Row Gateway (行记录入口)十分相似,但前者是领域模型,后者是一种数据源模式。关系型数据库往往通过外键来表述实体关系,Active Record 在数据源层面上也将这种关系映射为对象的关联和聚集。
Active Record 适合非常简单的领域需求,尤其在领域模型和数据库模型十分相似的情况下。如果遇到更加复杂的领域模型结构(例如用到继承、策略的领域模型),往往需要使用分离数据源的领域模型,结合 Data Mapper (数据映射器)使用。
Active Record 驱动框架一般兼有 ORM 框架的功能,但 ActivActive Recorde Record 不是简单的 ORM,正如和 Row Gateway 的区别。著名的例子是全栈(Full Stack) Web 开发框架 Ruby on Rails ,其默认使用一个纯 Ruby 写成的 Active Record 框架来驱动 MVC 中的模型层。
对象关系映射(ORM)提供了概念性的、易于理解的模型化数据的方法。ORM方法论基于三个核心原则: 简单:以最基本的形式建模数据。 传达性:数据库结构被任何人都能理解的语言文档化。 精确性:基于数据模型创建正确标准化了的结构。
在 Martin Fowler 的 《企业应用架构模式》 一书中曾详细叙述了本模式。
以下是著名的 Active Record 驱动框架:
SQLObject(Python)
Ruby on Rails ActiveRecord (Ruby)
Yii Framework ActiveRecord (PHP)
Castle ActiveRecord (.NET)
Migrations
Migrations are a convenient way for you to alter移动 your database in a structured and organized manner.Migrations是一种很便捷的方法让你能够以一种结构化的和有组织的方式来迁移你的数据库。You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run them.你可以手动编辑SQL片段,而且你有责任把这些告诉其他的开发人员,因为他们需要开发和使用它们。 You’d also have to keep track of which changes need to be run against the production machines next time you deploy.你也可以跟踪对你部署的代码在接下来的production机器(将会)发生的变化。
Active Record tracks which migrations have already been run so all you have to do is update your source and run rake db:migrate.Active Record跟踪并迁移你已经运行过的(代码和数据),而你只需要在更新了你的源代码的时候执行 rake db:migrate。 Active Record will work out which migrations should be run.Active Recor将会计算出那些迁移需要被执行。 It will also update your db/schema.rb file to match the structure of your database.它还会更新你的 db/schema.rb文件使其于你的数据库结构相匹配。
Rails使用的是 Active Record 框架来处理数据迁移,这里笔者把 Active Record 框架放在一个地方学习了,如需了解Migration部分需要直接阅读Migration部分。
Active Record Validations and Callbacks 活动记录验证和回调
This guide teaches you how to hook勾子 into the life cycle of your Active Record objects.这个教程指导你怎样挂接到你的 Active Record objects的生存周期。 You will learn how to validate the state of objects before they go into the database, and how to perform custom operations at certain points in the object life cycle.你将会学习到在将数据对象存入数据库之前怎样验证它们的状态,以及在对象生存周期的一些点上怎样执行定制操作。
Rails使用的是 Active Record 框架来处理验证和回调,这里笔者把 Active Record 框架放在一个地方学习了,如需了解Migration部分需要直接阅读Validations and Callbacks 部分。
A Guide to Active Record Associations
This guide covers the association features of Active Record. By referring to this guide, you will be able to:本教程涵盖了 Active Record的关系部分的特性。(通过)这个教程提及的,你将能够:
ñ Declare associations between Active Record models 在 Active Record的models中声明(它们的)关系
ñ Understand the various types of Active Record associations 明白各种类型的Active Record关系
ñ Use the methods added to your models by creating associations 通过添加方法到models(的形式)来创建关系
Active Record Query Interface(基于)活动记录的查询接口
This guide covers different ways to retrieve data from the database using Active Record. By referring to this guide, you will be able to:
这个教程涵盖了使用基于Active Record 的不同方式从数据库检索数据。同过参考这个教程,你可以掌握:
ñ Find records using a variety of methods and conditions 使用各种方法和条件查找记录
ñ Specify the order, retrieved attributes, grouping, and other properties of the found records对查找的记录指定顺序,检索属性,组,和其他属性
ñ Use eager急于 loading to reduce the number of database queries needed for data retrieval 使用预先加载,以减少数据检索所需的数据库查询数量
ñ Use dynamic finders methods 使用多元搜索方法
ñ Check for the existence of particular records 在特定的记录部分检查存在的记录
ñ Perform various calculations on Active Record models 在Active Record 模型中执行各种计算
This Guide is based on Rails 3.0. Some of the code shown here will not work in other versions of Rails.
If you’re used to using raw SQL to find database records then, generally, you will find that there are better ways to carry out进行 the same operations in Rails. Active Record insulates you from the need to use SQL in most cases.如果你使用过原生的SQL(语句)来查询数据库,那么,一般情况下,你将会发现(对数据库)进行同样的操作在Rails中会有这更好的方法。 Active Record在大多数情况下会让你远离你(以前)需要使用的SQL查询语句。
Code examples throughout this guide will refer to one or more of the following models:贯穿这个教材代码示例将会参考一个或多个下面的模型:
All of the following models use id as the primary key, unless specified otherwise.所有的模型都会使用id作为主键,除非指定了其他主键。
class Client < ActiveRecord::Base
has_one :address
has_many :orders
has_and_belongs_to_many :roles
end
class Address < ActiveRecord::Base
belongs_to :client
end
class Order < ActiveRecord::Base
belongs_to :client, :counter_cache => true
end
class Role < ActiveRecord::Base
has_and_belongs_to_many :clients
end
Active Record will perform queries on the database for you and is compatible兼容 with most database systems (MySQL, PostgreSQL and SQLite to name a few). Regardless of which database system you’re using, the Active Record method format will always be the same.
Active Record将会为你在数据库中执行查询并且它兼容大多数的数据库系统(MySQL, PostgreSQL and SQLite这里仅仅列举这些)。不管你使用的是何种数据库系统, Active Record的方法格式通常是相同的。
1 Retrieving Objects from the Database在数据库中检索对象
To retrieve objects from the database, Active Record provides several finder methods. Each finder method allows you to pass arguments into it to perform certain queries on your database without writing raw SQL.
为了从数据库中检索对象, Active Record提供了一些查询方法。每个查询方法都运行你带入参数并在数据库中执行查询而不用写SQL自身的语句。
The methods are:
ñ where
ñ select
ñ group
ñ order
ñ reorder
ñ reverse_order 逆向排序
ñ limit
ñ offset 偏移
ñ joins
ñ includes
ñ lock
ñ readonly
ñ from
ñ having
All of the above methods return an instance of ActiveRecord::Relation.所有以上方法会返回一个 ActiveRecord::Relation的实例。
Primary operation of Model.find(options) can be summarized as:
Model.find(options)主要的操作可以被概括为:
ñ Convert the supplied options to an equivalent SQL query. 转换提供的(查询)选项为等同的SQL查询
ñ Fire the SQL query and retrieve the corresponding results from the database. 开始SQL查询并且检索从数据库相应的结果
ñ Instantiate the equivalent Ruby object of the appropriate model for every resulting row. 把每个(数据库中原生的)结果实例化等同的Ruby对象
ñ Run after_find callbacks if any. 运行 after_find进行回调如果需要
1.1 Retrieving a Single Object检索单个对象
Active Record lets you retrieve a single object using five different ways.
Active Record让你可以使用五种不同的方法检索单个对象。
1.1.1 Using a Primary Key使用主键查询
Using Model.find(primary_key), you can retrieve the object corresponding to the supplied primary key and matching the supplied options (if any). For example:
使用Model.find(primary_key),你可以检索对象通过提供相应的主键或者匹配提供的其他选项(如果存在)。例如:
# Find the client with primary key (id) 1.
rails console
Loading development environment (Rails 3.1.1)
irb(main):001:0> p=Post.find(1)
Post Load (9.0ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT 1 [["id", 1]]
=> #<Post id: 1, name: "阿飞姐姐", title: "接口姐姐", content: "12342424", created_at: "2011-11-05 15:10:41", updated_at: "2011-11-05 15:10:41">
SQL equivalent of the above is:
SELECT * FROM posts WHERE (posts.id = 1)
Model.find(primary_key) will raise an ActiveRecord::RecordNotFound exception if no matching record is found.
Model.find(primary_key)如果没有记录匹配则会抛出一个 ActiveRecord::RecordNotFound异常。
1.1.2 first
Model.first finds the first record matched by the supplied options. For example:
Model.first找到与提供的选项匹配的第一条记录。例如:
client = Client.first
=> #<Client id: 1, first_name: "Lifo">
irb(main):018:0> Post.first
Post Load (0.4ms) SELECT "posts".* FROM "posts" LIMIT 1
=> #<Post id: 2, name: "jhjguxin", title: "test console", content: "A new post to test console", created_at: "2011-11-05 15:55:17", updated_at: "2011-11-05 15:55:17"
SQL equivalent of the above is:
SELECT * FROM clients LIMIT 1
Model.first returns nil if no matching record is found. No exception will be raised.
如果没有记录匹配Model.first会返回nil。不会抛出异常。
1.1.3 last
Model.last finds the last record matched by the supplied options. For example:
client = Client.last
=> #<Client id: 221, first_name: "Russel">
SQL equivalent of the above is:上面等同的SQL语句是:
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
##SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT 1
Model.last returns nil if no matching record is found. No exception will be raised.
如果没有记录匹配Model.last会返回nil。不会抛出异常。
1.2 Retrieving Multiple Objects检索多个目标
1.2.1 Using Multiple Primary Keys使用多个主键
Model.find(array_of_primary_key) also accepts an array of primary keys. An array of all the matching records for the supplied primary keys is returned. For example:
Model.find(array_of_primary_key)也接受一个主键数组。将会返回一个由所有与提供的主键匹配的记录组成的数组。例如:
# Find the clients with primary keys 1 and 10.
client = Client.find(1, 10) # Or even Client.find([1, 10])
=> [#<Client id: 1, first_name: => "Lifo">, #<Client id: 10, first_name: => "Ryan">]
SQL equivalent of the above is:
SELECT * FROM clients WHERE (clients.id IN (1,10))
Model.find(array_of_primary_key) will raise an ActiveRecord::RecordNotFound exception unless a matching record is found for all of the supplied primary keys.
Model.find(array_of_primary_key)只要有一条记录没有找到就会抛出 ActiveRecord::RecordNotFound异常
ActiveRecord::RecordNotFound: Couldn't find all Posts with IDs (2, 4, 5) (found 2 results, but was looking for 3)
1.3 Retrieving Multiple Objects in Batches分批次的检索多个目标
Sometimes you need to iterate反复重复 over a large set of records. For example to send a newsletter to all users, to export some data, etc.
有时候你需要遍历大量的记录,例如发送一条业务通讯给所有的用户,输出一些数据,等等。
The following may seem very straight forward at first:
首先(通过)以下内容看起来会非常直观
# Very inefficient when users table has thousands of rows.
User.all.each do |user|
NewsLetter.weekly_deliver(user)
end
But if the total number of rows in the table is very large, the above approach may vary from being under performant to just plain平原 impossible.
但是如果(数据)表单的行有非常大的量,上面的方法在执行(的时候)不可能性能(还是那么)平稳。
This is because User.all.each makes Active Record fetch the entire table, build a model object per row, and keep the entire array in the memory. Sometimes that is just too many objects and demands too much memory.
这是因为 User.all.each使得 Active Record获取整个表单,给每一行数据创建一个object model,并且保留整个数组在内存中。有时会有太多的对象并且需要太多的内存。
Configuring the batch size配置批次大小
Behind the scenes find_each fetches rows in batches of 1000 and yields them one by one. The size of the underlying batches is configurable via the :batch_size option.在使用 find_each获取1000次记录行并且一个接一个的yield它们的情况中。(确定)下面(查找)批次的大小是通过配置:batch_size选项。
To fetch User records in batch size of 5000:
User.find_each(:batch_size => 5000) do |user|
NewsLetter.weekly_deliver(user)
end
Starting batch find from a specific primary key通过一个指定的主键开始批次查找
Records are fetched in ascending order on the primary key, which must be an integer. The :start option allows you to configure the first ID of the sequence if the lowest is not the one you need. This may be useful for example to be able to resume an interrupted batch process if it saves the last processed ID as a checkpoint.这会非常有用比如能够减少因为设置最后处理的ID作为checkpoint引起的中断。
(这里)是按照主键值的升序排列获取记录的,主键值必须是整数。:start选项允许你配置序列的开始ID如果排序最低的(记录)不是你需要的。
To send newsletters only to users with the primary key starting from 2000:
User.find_each(:batch_size => 5000, :start => 2000) do |user|
NewsLetter.weekly_deliver(user)
end
Additional options其他(附加)选项
find_each accepts the same options as the regular find method. However, :order and :limit are needed internally and hence not allowed to be passed explicitly.
find_each接受和正则find方法相同的选项。
1.3.2 find_in_batches
You can also work by chunks instead of row by row using find_in_batches. This method is analogous to find_each, but it yields arrays of models instead:
通过使用find_in_batches你也可以用chunks替代数据行。这个方法类似于 find_each,但是作为替代它(会输出)到一个数组区域:
# Works in chunks of 1000 invoices at a time.
Invoice.find_in_batches(:include => :invoice_lines) do |invoices|
export.add_invoices(invoices)
end
The above will yield the supplied block with 1000 invoices every time.上面的语句每次会提供给语句1000invoices。
2 Conditions条件
The where method allows you to specify conditions to limit the records returned, representing the WHERE-part of the SQL statement. Conditions can either be specified as a string, array, or hash.
where方法允许你指定条件限制记录返回(的内容),表示SQL部分的WHERE部分。条件可以指定为一个字符串,数组,或者hash(字典)。
2.1 Pure String Conditions纯字符串条件
If you’d like to add conditions to your find, you could just specify them in there, just like Client.where("orders_count = '2'"). This will find all clients where the orders_count field’s value is 2.
Building your own conditions as pure strings can leave you vulnerable脆弱 to SQL injection注入 exploits漏洞. For example, Client.where("first_name LIKE '%#{params[:first_name]}%'") is not safe. See the next section for the preferred way to handle conditions using an array.
2.2 Array Conditions
Now what if that number could vary, say as an argument from somewhere? The find then becomes something like:
现在,如果这个数字可能会有所不同,(比如说)作为某个地方的一个参数?查找会变成如下:
Client.where("orders_count = ?", params[:orders])
Active Record will go through the first element in the conditions value and any additional elements will replace the question marks (?) in the first element.
Active Record将会在第一个元素中表示条件(语句)并且其他元素取代第一个元素中的问号。
Or if you want to specify two conditions, you can do it like:
Client.where("orders_count = ? AND locked = ?", params[:orders], false)
In this example, the first question mark will be replaced with the value in params[:orders] and the second will be replaced with the SQL representation of false, which depends on the adapter.
The reason for doing code like:
Client.where("orders_count = ?", params[:orders])
instead of:
Client.where("orders_count = #{params[:orders]}")
is because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database as-is. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string.
(这里)是基于参数安全(考虑)。将变量直接放入条件字符串将会(原封不动的传送变量)到数据库。他的意思是这个参数有可能将会是以一个来自用户的恶意的非转义的变量。如果你这样做,你就把整个数据库放在了风险之中,因为一旦有用户找到他们或者它可以利用(漏洞)对你数据库做任何操作。永远不要直接把你的参数放在条件字符串中。
For more information on the dangers of SQL injection, see the Ruby on Rails Security Guide.
2.2.1 Placeholder Conditions条件(参数)占位符
Similar to the (?) replacement style of params, you can also specify keys/values hash in your array conditions:最简单的是使用(?)替代参数的形式,你也可以指定 keys/values hash在你的条件语句数组中:
Client.where("created_at >= :start_date AND created_at <= :end_date",
{:start_date => params[:start_date], :end_date => params[:end_date]})
This makes for clearer readability if you have a large number of variable conditions.当你有大量的条件变量时这样表示更加简洁和可读性更好。
2.2.2 Range Conditions范围条件
If you’re looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the IN SQL statement for this. If you had two dates coming in from a controller you cou
展开阅读全文