翻译自SUN官方文档
Topics
主题
• What is Active Record?
什么是Active Record?
• ActiveRecord Object Creation
ActiveRecord对象创建
• Find operation
Find方法
• Dynamic Attribute-based Finders
动态地基于属性的Finder
• Validation
验证
• Migration
迁移
• Callbacks
回滚
• Exception Handling
异常控制
What is Active Record?
What is Active Record?
• Active Record is a Ruby library that provides mapping between business objects and database tables
Active Record是一个提供业务对象和数据库表之间映射关系的Ruby库
> Accessing, saving, creating, updating operations in your Rails code are performed by Active Record
Rails代码里的增删改查操作由Active Record执行
• It‘s an implementation of the object-relational mapping (ORM) pattern by the same name as described by Martin Fowler:
它实现了对象关系映射(ORM)模型,通过相同的命名由Martin Fowler:
> "An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data."
“一个对象,就是数据库表或视图里的一行,封装了数据库链接,和数据的逻辑域”
• Contains domain logic
包含逻辑域
Major Features
主要特性
• Automated mapping between classes and tables, attributes and columns.
在类和表,属性和列之间自动映射
• Validation rules that can differ for new or existing objects.
验证规则可以区别新旧对象
• Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc).
方法或队列的在一个完整生命周期里可以回滚
• Observers for the entire lifecycle
生命周期的观察者
• Transaction support on both a database and object level.
数据库和对象层都有事务支持
• Reflections on columns, associations, and aggregations
反射
• Direct manipulation (instead of service invocation)
直接操作
• Database abstraction through simple adapters (~100 lines) with a shared connector
通过简单的adapter实现数据抽象
• Logging support for Log4r and Logger
Log4r和Logger的日志支持
• Associations between objects controlled by simple meta-programming macros.
通过简单的元编程实现对象控制的联合
• Aggregations of value objects controlled by simple meta-programming macros.
通过简单的元编程实现对象控制的聚合
• Inheritance hierarchies
继承的分层
Automatic Mapping between Classes and Tables
类和表之间的自动映射
Mapping between Classes and Tables
• Class definition below is automatically mapped to the table named “products”
下面的类自动映射到命名为“products”的表
• Schema of “products” table
数据模型的“products”表
Active Record Definition & Domain Logic
Active Record定义和逻辑域
• Class definition below is automatically mapped to the table named “products”
class Product < ActiveRecord::Base
end
• Active Record typically contain domain logic
典型的Active Record包括逻辑域
Attributes
属性
• Active Record objects don‘t specify their attributes directly, but rather infer them from the table definition with which they‘re linked.
Active Record对象并不直接指定他们的属性,而是从相关的表的定义中推断
• Adding, removing, and changing attributes and their type is done directly in the database.
添加,删除和改变属性和类型会直接在数据库里完成
• Any change is instantly reflected in the Active Record objects.
任何改变将立刻在Active Record里产生影响
ActiveRecord Object Creation, Update, Delete
ActiveRecord对象的创建,修改和删除
ActiveRecord Objects Can Be Created in 3 Different Ways
ActiveRecord对象能够由三种途径创建
• Constructor parameters in a hash
构造函数在一个hash
• Use block initialization
用block初始化
• Create a bare object and then set attributes
创建一个裸体对象,然后设置属性
ActiveRecord Objects Can Be Saved
ActiveRecord对象能够被保存
• Use save instance method - a row is added to the table
用save方法 - 一行将被添加到数据库
ActiveRecord Find Operation
ActiveRecord的Find操作
find Method
find方法
• find is a class method
find是一个类方法
> In the same way, new and create are class methods
同理,new和create也是类方法
> You use it with a ActiveRecord class (not with an object instance)
你使用它用ActiveRecord类(而不是实例变量)
>Product.find(..)
Find Operation
Find操作
• Find operates with four different retrieval approaches:
Find操作有四个不同的实现方法:
> Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). If no record can be found for all of the listed ids, then RecordNotFound will be raised.
Find by id - 可以是一个指定的id(1),一个id列表(1, 5, 6), 或者是一个id数组([5, 6, 10]).如果一条记录也找不到,会抛出RecordNotFound异常
> Find first - This will return the first record matched by the options used
Find first - 返回第一条匹配的记录
> Find last - This will return the last record matched by the options used.
Find last - 返回最后一条匹配的记录
> Find all - This will return all the records matched by the options used. If no records are found, an empty array is returned.
Find all - 返回所有匹配的记录。如果找不到记录,返回一个空数组
Find Criteria
Find准则
• :conditions - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]
:conditions - 一条SQL语句,比如"administrator = 1" 或者 [ "user_name = ?", username ]
• :order
• :group - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
:group - 一个需要被分组的属性名。用GROUP BY SQL语句
• :limit - An integer determining the limit on the number of rows that should be returned.
:limit - 给一个整数决定需要返回的行数
• :offset - An integer determining the offset from where the rows should be fetched.
:offset - 给一个整数决定哪几行被分离。
• :joins - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed) or named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s).
:joins - 附加一个SQL语句,如"LEFT JOIN comments ON comments.post_id = id"(必须的)或者由同一个格式命名
• :include - Names associations that should be loaded alongside. The symbols named refer to already defined associations.
• :select - By default, this is "*" as in "SELECT * FROM", but can be changed if you, for example, want to do a join but not include the joined columns.
• :from - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name of a database view).
• :readonly - Mark the returned records read-only so they cannot be saved or updated.
• :lock - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE". :lock => true gives connection‘s default exclusive lock, usually "FOR UPDATE".
Examples: Find by id
Examples: Find first
Example: Find all
Find with lock
• Imagine two concurrent transactions: each will read person.visits == 2, add 1 to it, and save, resulting in two saves of person.visits = 3. By locking the row, the second transaction has to wait until the first is finished; we get the expected person.visits == 4.
Conditions
• Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
条件能够被赋予字符串,数组,或者hash
> The array form is to be used when the condition input is tainted and requires sanitization.
> The string form can be used for statements that don‘t involve tainted data.
> The hash form works much like the array form, except only equality and range is possible. Examples:
Examples: Conditions
• Array form
> User.find(:all, :conditions=>["hobby=? AND name=?", 'swimming', 'Tom']
• String form
> User.find(:all, :conditions=>"hobby='swimming'", :order=>"hobby DESC, age")
• Hash form
> User.find(:all, :conditions=>{:hobby=>'swimming', :name=>'Tom'}, :order=>"hobby DESC, age")
Dynamic Attributebased Finders
Dynamic Attribute-based Finders
• Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL.
• They work by appending the name of an attribute to find_by_ or find_all_by_, so you get finders like
Person.find_by_user_name, Person.find_all_by_last_name, and Payment.find_by_transaction_id.
Dynamic Find Operation
• So instead of writing Person.find(:first, :conditions => ["user_name = ?", user_name]), you just do Person.find_by_user_name(user_name).
• And instead of writing Person.find(:all, :conditions => ["last_name = ?", last_name]), you just do Person.find_all_by_last_name(last_name).
ActiveRecord Validation
ActiveRecord::Validations
• Validation methods
Validation
ActiveRecord::Validations
• Active Records implement validation by overwriting Base#validate (or the variations, validate_on_create and validate_on_update).
Active Records实现了验证通过重写Base#validate
ActiveRecord Migration
ActiveRecord Migration
• Manage the evolution of a schema used
> It’s a solution to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to push that change to other developers and to the production server.
这是个解决添加成员在本地数据库的方法
• You can describe the transformations in self-contained classes that can be checked into version control systems and executed against another database that might be one, two, or five versions behind.
Example: Migration
• Add a boolean flag to the accounts table and remove it again, if you’re backing out of the migration.
Example: Migration
• First adds the system_settings table, then creates the very first row in it using the Active Record model that relies on the table.
Available Transformations
• create_table(name, options)
• drop_table(name)
• rename_table(old_name, new_name)
• add_column(table_name, column_name, type, options)
• rename_column(table_name, column_name, new_column_name)
• change_column(table_name, column_name, type, options)
• remove_column(table_name, column_name)
• add_index(table_name, column_names, index_type, index_name)
• remove_index(table_name, index_name)
Callbacks
回滚
What is Callback?
• Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic before or after an alteration of the object state.
回滚就是一个生命周期的一个点,可以在发生改变后混滚到该点
• This can be used to make sure that associated and dependent objects are deleted when destroy is called (by overwriting before_destroy) or to massage attributes before they‘re validated (by overwriting before_validation).
Lifecycle of an ActiveRecord Object
• (-) save
• (-) valid
• (1) before_validation
• (2) before_validation_on_create
• (-) validate
• (-) validate_on_create
• (3) after_validation
• (4) after_validation_on_create
• (5) before_save
• (6) before_create
• (-) create
• (7) after_create
• (8) after_save
Example: Callbacks in a Model
• The callback before_validation_on_create gets called on create.
Exception Handling
异常处理
Exception Handling
• Handle RecordNotFound Exception
Topics
主题
• What is Active Record?
什么是Active Record?
• ActiveRecord Object Creation
ActiveRecord对象创建
• Find operation
Find方法
• Dynamic Attribute-based Finders
动态地基于属性的Finder
• Validation
验证
• Migration
迁移
• Callbacks
回滚
• Exception Handling
异常控制
What is Active Record?
What is Active Record?
• Active Record is a Ruby library that provides mapping between business objects and database tables
Active Record是一个提供业务对象和数据库表之间映射关系的Ruby库
> Accessing, saving, creating, updating operations in your Rails code are performed by Active Record
Rails代码里的增删改查操作由Active Record执行
• It‘s an implementation of the object-relational mapping (ORM) pattern by the same name as described by Martin Fowler:
它实现了对象关系映射(ORM)模型,通过相同的命名由Martin Fowler:
> "An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data."
“一个对象,就是数据库表或视图里的一行,封装了数据库链接,和数据的逻辑域”
• Contains domain logic
包含逻辑域
Major Features
主要特性
• Automated mapping between classes and tables, attributes and columns.
在类和表,属性和列之间自动映射
• Validation rules that can differ for new or existing objects.
验证规则可以区别新旧对象
• Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc).
方法或队列的在一个完整生命周期里可以回滚
• Observers for the entire lifecycle
生命周期的观察者
• Transaction support on both a database and object level.
数据库和对象层都有事务支持
• Reflections on columns, associations, and aggregations
反射
• Direct manipulation (instead of service invocation)
直接操作
• Database abstraction through simple adapters (~100 lines) with a shared connector
通过简单的adapter实现数据抽象
• Logging support for Log4r and Logger
Log4r和Logger的日志支持
• Associations between objects controlled by simple meta-programming macros.
通过简单的元编程实现对象控制的联合
• Aggregations of value objects controlled by simple meta-programming macros.
通过简单的元编程实现对象控制的聚合
• Inheritance hierarchies
继承的分层
Automatic Mapping between Classes and Tables
类和表之间的自动映射
Mapping between Classes and Tables
• Class definition below is automatically mapped to the table named “products”
下面的类自动映射到命名为“products”的表
- class Product < ActiveRecord::Base
- end
数据模型的“products”表
- CREATE TABLE products (
- id int(11) NOT NULL auto_increment,
- name varchar(255),
- PRIMARY KEY (id)
- );
Active Record定义和逻辑域
• Class definition below is automatically mapped to the table named “products”
class Product < ActiveRecord::Base
end
• Active Record typically contain domain logic
典型的Active Record包括逻辑域
- class Product < ActiveRecord::Base
- def my_business_method
- # Whatever business logic
- end
- end
属性
• Active Record objects don‘t specify their attributes directly, but rather infer them from the table definition with which they‘re linked.
Active Record对象并不直接指定他们的属性,而是从相关的表的定义中推断
• Adding, removing, and changing attributes and their type is done directly in the database.
添加,删除和改变属性和类型会直接在数据库里完成
• Any change is instantly reflected in the Active Record objects.
任何改变将立刻在Active Record里产生影响
ActiveRecord Object Creation, Update, Delete
ActiveRecord对象的创建,修改和删除
ActiveRecord Objects Can Be Created in 3 Different Ways
ActiveRecord对象能够由三种途径创建
• Constructor parameters in a hash
构造函数在一个hash
- user = User.new(:name => "David", :occupation => "Artist")
用block初始化
- user = User.new do |u|
- u.name = "David"
- u.occupation = "Code Artist"
- end
创建一个裸体对象,然后设置属性
- user = User.new
- user.name = "David"
- user.occupation = "Code Artist"
ActiveRecord对象能够被保存
• Use save instance method - a row is added to the table
用save方法 - 一行将被添加到数据库
- user = User.new(:name => "David", :occupation => "Artist")
- user.save
ActiveRecord Find Operation
ActiveRecord的Find操作
find Method
find方法
• find is a class method
find是一个类方法
> In the same way, new and create are class methods
同理,new和create也是类方法
> You use it with a ActiveRecord class (not with an object instance)
你使用它用ActiveRecord类(而不是实例变量)
>Product.find(..)
Find Operation
Find操作
• Find operates with four different retrieval approaches:
Find操作有四个不同的实现方法:
> Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). If no record can be found for all of the listed ids, then RecordNotFound will be raised.
Find by id - 可以是一个指定的id(1),一个id列表(1, 5, 6), 或者是一个id数组([5, 6, 10]).如果一条记录也找不到,会抛出RecordNotFound异常
> Find first - This will return the first record matched by the options used
Find first - 返回第一条匹配的记录
> Find last - This will return the last record matched by the options used.
Find last - 返回最后一条匹配的记录
> Find all - This will return all the records matched by the options used. If no records are found, an empty array is returned.
Find all - 返回所有匹配的记录。如果找不到记录,返回一个空数组
Find Criteria
Find准则
• :conditions - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]
:conditions - 一条SQL语句,比如"administrator = 1" 或者 [ "user_name = ?", username ]
• :order
• :group - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
:group - 一个需要被分组的属性名。用GROUP BY SQL语句
• :limit - An integer determining the limit on the number of rows that should be returned.
:limit - 给一个整数决定需要返回的行数
• :offset - An integer determining the offset from where the rows should be fetched.
:offset - 给一个整数决定哪几行被分离。
• :joins - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed) or named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s).
:joins - 附加一个SQL语句,如"LEFT JOIN comments ON comments.post_id = id"(必须的)或者由同一个格式命名
• :include - Names associations that should be loaded alongside. The symbols named refer to already defined associations.
• :select - By default, this is "*" as in "SELECT * FROM", but can be changed if you, for example, want to do a join but not include the joined columns.
• :from - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name of a database view).
• :readonly - Mark the returned records read-only so they cannot be saved or updated.
• :lock - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE". :lock => true gives connection‘s default exclusive lock, usually "FOR UPDATE".
Examples: Find by id
- Person.find(1) # returns the object for ID = 1
- Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
- Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
- Person.find([1]) # returns an array for the object with ID = 1
- Person.find(1, :conditions => "administrator = 1",
- :order => "created_on DESC")
- Person.find(:first) # returns the first object fetched
- # by SELECT * FROM people
- Person.find(:first, :conditions => [ "user_name = ?", user_name])
- Person.find(:first, :order => "created_on DESC", :offset => 5)
- Person.find(:all) # returns an array of objects for all the
- rows fetched by SELECT * FROM people
- Person.find(:all, :conditions => [ "category IN (?)",
- categories], :limit => 50)
- Person.find(:all, :conditions => { :friends => ["Bob",
- "Steve", "Fred"] }
- Person.find(:all, :offset => 10, :limit => 10)
- Person.find(:all, :include => [ :account, :friends ])
- Person.find(:all, :group => "category")
• Imagine two concurrent transactions: each will read person.visits == 2, add 1 to it, and save, resulting in two saves of person.visits = 3. By locking the row, the second transaction has to wait until the first is finished; we get the expected person.visits == 4.
- Person.transaction do
- person = Person.find(1, :lock => true)
- person.visits += 1
- person.save!
- end
• Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
条件能够被赋予字符串,数组,或者hash
> The array form is to be used when the condition input is tainted and requires sanitization.
> The string form can be used for statements that don‘t involve tainted data.
> The hash form works much like the array form, except only equality and range is possible. Examples:
Examples: Conditions
• Array form
> User.find(:all, :conditions=>["hobby=? AND name=?", 'swimming', 'Tom']
• String form
> User.find(:all, :conditions=>"hobby='swimming'", :order=>"hobby DESC, age")
• Hash form
> User.find(:all, :conditions=>{:hobby=>'swimming', :name=>'Tom'}, :order=>"hobby DESC, age")
Dynamic Attributebased Finders
Dynamic Attribute-based Finders
• Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL.
• They work by appending the name of an attribute to find_by_ or find_all_by_, so you get finders like
Person.find_by_user_name, Person.find_all_by_last_name, and Payment.find_by_transaction_id.
Dynamic Find Operation
• So instead of writing Person.find(:first, :conditions => ["user_name = ?", user_name]), you just do Person.find_by_user_name(user_name).
• And instead of writing Person.find(:all, :conditions => ["last_name = ?", last_name]), you just do Person.find_all_by_last_name(last_name).
ActiveRecord Validation
ActiveRecord::Validations
• Validation methods
- class User < ActiveRecord::Base
- validates_presence_of :username, :level
- validates_uniqueness_of :username
- validates_oak_id :username
- validates_length_of :username, :maximum => 3, :allow_nil
- validates_numericality_of :value, :on => :create
- end

ActiveRecord::Validations
• Active Records implement validation by overwriting Base#validate (or the variations, validate_on_create and validate_on_update).
Active Records实现了验证通过重写Base#validate
- class Person < ActiveRecord::Base
- protected
- def validate
- errors.add_on_empty %w( first_name last_name )
- errors.add("phone_number", "has invalid format") unless phone_number =~ /[0-9]*/
- end
- def validate_on_create # is only run the first time a new object is saved
- unless valid_discount?(membership_discount)
- errors.add("membership_discount", "has expired")
- end
- end
- end
ActiveRecord Migration
ActiveRecord Migration
• Manage the evolution of a schema used
> It’s a solution to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to push that change to other developers and to the production server.
这是个解决添加成员在本地数据库的方法
• You can describe the transformations in self-contained classes that can be checked into version control systems and executed against another database that might be one, two, or five versions behind.
Example: Migration
• Add a boolean flag to the accounts table and remove it again, if you’re backing out of the migration.
- class AddSsl < ActiveRecord::Migration
- def self.up
- add_column :accounts, :ssl_enabled, :boolean, :default => 1
- end
- def self.down
- remove_column :accounts, :ssl_enabled
- end
- end
Example: Migration
• First adds the system_settings table, then creates the very first row in it using the Active Record model that relies on the table.
- class AddSystemSettings < ActiveRecord::Migration
- def self.up
- create_table :system_settings do |t|
- t.column :name, :string
- t.column :label, :string
- t.column :value, :text
- end
- SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
- end
- def self.down
- drop_table :system_settings
- end
- end
• create_table(name, options)
• drop_table(name)
• rename_table(old_name, new_name)
• add_column(table_name, column_name, type, options)
• rename_column(table_name, column_name, new_column_name)
• change_column(table_name, column_name, type, options)
• remove_column(table_name, column_name)
• add_index(table_name, column_names, index_type, index_name)
• remove_index(table_name, index_name)
Callbacks
回滚
What is Callback?
• Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic before or after an alteration of the object state.
回滚就是一个生命周期的一个点,可以在发生改变后混滚到该点
• This can be used to make sure that associated and dependent objects are deleted when destroy is called (by overwriting before_destroy) or to massage attributes before they‘re validated (by overwriting before_validation).
Lifecycle of an ActiveRecord Object
• (-) save
• (-) valid
• (1) before_validation
• (2) before_validation_on_create
• (-) validate
• (-) validate_on_create
• (3) after_validation
• (4) after_validation_on_create
• (5) before_save
• (6) before_create
• (-) create
• (7) after_create
• (8) after_save
Example: Callbacks in a Model
• The callback before_validation_on_create gets called on create.
- class User < ActiveRecord::Base
- # Strip everything but alphabets
- def before_validation_on_create
- self.name = name.gsub(/[^A-Za-z]/, "") if attribute_present?("name")
- end
- # More code
- end
Exception Handling
异常处理
Exception Handling
• Handle RecordNotFound Exception
- begin
- User.find(2345)
- rescue ActiveRecord::RecordNotFound
- outs “Not found!”
- end