学习目标:掌握 ActiveRecord 关联的实现原理和最佳实践
引言#
关联(Associations)是 ActiveRecord 最强大的特性之一,它让我们能够优雅地表达对象之间的关系。
1. 关联类型#
1.1 belongs_to - 属于关系#
class Post < ApplicationRecord
belongs_to :user
end
# 生成的方法:
post.user # 获取关联的user
post.user = user # 设置关联
post.build_user # 构建新user
post.create_user # 创建并保存user
post.reload_user # 重新加载
数据库结构:
create_table :posts do |t|
t.references :user, foreign_key: true
end
1.2 has_one - 一对一#
class User < ApplicationRecord
has_one :profile
end
# 生成方法与 belongs_to 类似
user.profile
user.profile = profile
user.build_profile
user.create_profile
1.3 has_many - 一对多#
class User < ApplicationRecord
has_many :posts
end
user.posts # 获取所有posts
user.posts << post # 添加post
user.posts.build # 构建新post
user.posts.create # 创建并保存
user.posts.count # 计数
user.posts.clear # 清空
user.posts.destroy_all # 删除所有
1.4 has_many :through - 多对多#
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
1.5 多态关联#
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
class Post < ApplicationRecord
has_many :comments, as: :commentable
end
class Photo < ApplicationRecord
has_many :comments, as: :commentable
end
# 数据库结构
create_table :comments do |t|
t.references :commentable, polymorphic: true
end
2. 关联选项#
2.1 常用选项#
class User < ApplicationRecord
has_many :posts,
dependent: :destroy, # 删除策略
foreign_key: 'author_id', # 外键名
class_name: 'Article', # 关联类名
inverse_of: :author, # 反向关联
counter_cache: true, # 计数缓存
touch: true # 更新时间戳
end
2.2 dependent 选项#
has_many :posts, dependent: :destroy # 删除关联记录
has_many :posts, dependent: :delete_all # 直接删除(不触发回调)
has_many :posts, dependent: :nullify # 设置外键为NULL
has_many :posts, dependent: :restrict_with_error # 有关联时阻止删除
3. 预加载策略#
3.1 三种预加载方式#
# includes - 智能选择
Post.includes(:user)
# preload - 分离查询
Post.preload(:user)
# eager_load - JOIN查询
Post.eager_load(:user)
3.2 嵌套预加载#
Post.includes(user: [:profile, :company])
Post.includes(:user, comments: :author)
4. 实现原理#
4.1 宏的实现#
# activerecord/lib/active_record/associations.rb
module ClassMethods
def belongs_to(name, scope = nil, **options)
reflection = Builder::BelongsTo.build(self, name, scope, options)
Reflection.add_reflection(self, name, reflection)
end
end
4.2 Association 类#
# activerecord/lib/active_record/associations/association.rb
class Association
attr_reader :owner, :reflection
def initialize(owner, reflection)
@owner = owner
@reflection = reflection
end
def load_target
# 加载关联对象
end
end
5. 本章总结#
关联是 ActiveRecord 的核心特性,理解其实现原理对优化查询至关重要。
上一篇:ActiveRecord 查询接口 下一篇:ActiveModel 接口 返回:学习指南首页