我在Rails(4.2.4)(带MySQL)中急切地使用ActiveRecord加载连接表时遇到问题。问题是连接表使用了复合主键,这会导致一些奇怪的问题?急切负荷的行为。这是一个澄清的例子:
class Student < ActiveRecord::Base
has_many :course_student_joins
has_many :courses, through: :course_student_joins
end
class Course < ActiveRecord::Base
has_many :course_student_joins
has_many :students, through: :course_student_joins
end
class CourseStudentJoin < ActiveRecord::Base
belongs_to :course
belongs_to :student
end
Table: courses
id name
1 Course 1
2 Course 2
Table: students
id name
1 Joey
2 Johnny
3 Dee Dee
Table: course_student_joins
course_id student_id extra_id
1 1 5
2 2 6
所以course_student_joins是一个经典的多对多连接表。这里有趣的是,如果course_student_joins表上的PRIMARY KEY只包含course_id(单个字段),则可以正常工作:
> Course.eager_load(:course_student_joins).first.course_student_joins
Result:
SQL (0.2ms) SELECT `courses`.`id` AS t0_r0, `courses`.`name` AS t0_r1, `course_student_joins`.`course_id` AS t1_r0, `course_student_joins`.`student_id` AS t1_r1, `course_student_joins`.`extra_id` AS t1_r2 FROM `courses` LEFT OUTER JOIN `course_student_joins` ON `course_student_joins`.`course_id` = `courses`.`id` WHERE `courses`.`id` = 1 AND `courses`.`id` IN (1)
=> #<ActiveRecord::Associations::CollectionProxy [#<CourseStudentJoin course_id: 1, student_id: 1, extra_id: 5>]>
如果PRIMARY KEY被更改(数据库方面)为(course_id,student_id)的复合键,这就是我们想要的(因为课程不能让同一个学生两次,主键需要是这是我们得到的:
> Course.eager_load(:course_student_joins).find_by(id:1).course_student_joins
Result:
SQL (0.2ms) SELECT `courses`.`id` AS t0_r0, `courses`.`name` AS t0_r1, `course_student_joins`.`course_id` AS t1_r0, `course_student_joins`.`student_id` AS t1_r1, `course_student_joins`.`extra_id` AS t1_r2 FROM `courses` LEFT OUTER JOIN `course_student_joins` ON `course_student_joins`.`course_id` = `courses`.`id` WHERE `courses`.`id` = 1 AND `courses`.`id` IN (1)
=> #<ActiveRecord::Associations::CollectionProxy []>
请注意第二个结果中的空集合,以及两者(预期)中保持完全相同的SQL。
我们在has_many声明中使用了foreign_key,primary_key设置,甚至还有一个&#39;解决方案&#39;更改加载以反映它在rails 2.0及更低版本中的执行方式。也使用包含/引用。没有运气,任何/所有帮助表示赞赏。
最终目标是提供一个ActiveRecord课程集合,其中包括热心加入的学生和连接表中的条目,因为我们需要在ActiveModel :: Serializer中的连接表中操作一些值,我们将通过收集。
答案 0 :(得分:0)
最友好的Rails方法是自动递增id
整数主键+ (course_id, student_id)
上的唯一索引。
您是否有任何理由想要加载和CourseStudentJoin
记录?看起来你不想直接操纵它们,而且最好不要做像
Course.includes(:students).where(active: true, category: "Math").foo.bar
Student.includes(:courses).where(state: "CA").foo.bar
答案 1 :(得分:0)
感觉您不需要连接表的主键。您只需在迁移文件中指定Factories
即可。
还要注意更改数据库参数()而不告诉有关这些更改的rails架构,您可以通过干运行来id: false
您要寻找的最后一件事是HABTM关系。 has_and_belongs_to_many:http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
您需要的只是:
rake db:migrate
答案 2 :(得分:0)
跟进。将单个自动递增列作为永远不会使用的主键,并不合适。 composite_primary_keys gem确实会影响ActiveRecord中的eager_load机制并解决问题。我们没有考虑它,因为我们不需要gem的主要功能,但它确实以最可接受的方式解决了这个问题。