複合キーを使用して多対多を構築する方法のこの記事を読みました。エンティティは
class Student {
// ...
@OneToMany(mappedBy = "student")
Set<CourseRating> ratings;
// ...
}
class Course {
// ...
@OneToMany(mappedBy = "course")
Set<CourseRating> ratings;
// ...
}
@Entity
class CourseRating {
@EmbeddedId
CourseRatingKey id;
@ManyToOne
@MapsId("student_id")
@JoinColumn(name = "student_id")
Student student;
@ManyToOne
@MapsId("course_id")
@JoinColumn(name = "course_id")
Course course;
int rating;
// standard constructors, getters, and setters
}
@Embeddable
class CourseRatingKey implements Serializable {
@Column(name = "student_id")
Long studentId;
@Column(name = "course_id")
Long courseId;
// standard constructors, getters, and setters
// hashcode and equals implementation
}
ここで、特定のコースIDを持つ学生を取得したいとします。
私のJPQはどのように見えますか?
select s from Student s join fetch s.ratings r where r.id.courseId=:courserId
または
select s from Student s join fetch s.ratings r where r.course.id=:courserId
それともまったく違うのでしょうか?
この種のJPAクエリでは、SQLを注意深くチェックする必要があります。おそらく、予想以上に多くのことが起こっているからです。
ManyToMany
属性rating
との関係を適切に設定すると、2番目のクエリは機能しますが、問題が発生し、パフォーマンスが低下する可能性があります。
学生だけを取得するための正しいクエリは
em.createQuery("select s from Student s join fetch s.ratings r where r.course.id = 1", Student.class).getResultList().forEach(s->System.out.println(s + ":" + s.getRatings()));
それはログを与えます
Hibernate: select student0_.id as id1_2_0_, ratings1_.course_id as course_i1_1_1_, ratings1_.student_id as student_2_1_1_, ratings1_.rating as rating3_1_1_, ratings1_.student_id as student_2_1_0__, ratings1_.course_id as course_i1_1_0__ from Student student0_ inner join CourseRating ratings1_ on student0_.id=ratings1_.student_id where ratings1_.course_id=1
Hibernate: select course0_.id as id1_0_0_ from Course course0_ where course0_.id=?
Student(id=1):[CourseRating(id=model.CourseRatingKey@dd5, student=Student(id=1), course=Course(id=1), rating=11)]
Student(id=2):[CourseRating(id=model.CourseRatingKey@e10, student=Student(id=2), course=Course(id=1), rating=21)]
JPAはプリフェッチされていないため、コース情報を取得するための追加のクエリを生成します。自分でプリフェッチすることで解決できます。
em.createQuery("select s from Student s join fetch s.ratings r join fetch r.course c where c.id = 1", Student.class).getResultList().forEach(s->System.out.println(s + ":" + s.getRatings()));
そしてそれはログを与えます
Hibernate: select student0_.id as id1_2_0_, ratings1_.course_id as course_i1_1_1_, ratings1_.student_id as student_2_1_1_, course2_.id as id1_0_2_, ratings1_.rating as rating3_1_1_, ratings1_.student_id as student_2_1_0__, ratings1_.course_id as course_i1_1_0__ from Student student0_ inner join CourseRating ratings1_ on student0_.id=ratings1_.student_id inner join Course course2_ on ratings1_.course_id=course2_.id where course2_.id=1
Student(id=1):[CourseRating(id=model.CourseRatingKey@dd5, student=Student(id=1), course=Course(id=1), rating=11)]
Student(id=2):[CourseRating(id=model.CourseRatingKey@e10, student=Student(id=2), course=Course(id=1), rating=21)]
コース評価の一部しか設定されていない学生がいるという問題があります。学生の評価を更新しようとすると、他のコースの評価が失われると思います。おそらくあなたのユースケースに問題はありませんが、学生のリストでコースを取得することもできます:
em.createQuery("select distinct c from Course c join fetch c.ratings r join fetch r.student where c.id = 1", Course.class).getSingleResult().getRatings().forEach(r->System.out.println(r.getStudent() + ":" + r));
そして、少し異なるクエリでも同じ結果が得られ、他のコースに影響を与えずに学生の評価を更新できます。
Hibernate: select distinct course0_.id as id1_0_0_, ratings1_.course_id as course_i1_1_1_, ratings1_.student_id as student_2_1_1_, student2_.id as id1_2_2_, ratings1_.rating as rating3_1_1_, ratings1_.course_id as course_i1_1_0__, ratings1_.student_id as student_2_1_0__ from Course course0_ inner join CourseRating ratings1_ on course0_.id=ratings1_.course_id inner join Student student2_ on ratings1_.student_id=student2_.id where course0_.id=1
Student(id=2):CourseRating(id=model.CourseRatingKey@e10, student=Student(id=2), course=Course(id=1), rating=21)
Student(id=1):CourseRating(id=model.CourseRatingKey@dd5, student=Student(id=1), course=Course(id=1), rating=11)
RESTサービスを作成している場合、これはJSONフォーマットにも影響します。最初のインスタンスでは、1つのエントリを持つCourseRatingsの複数の配列があり、2番目のインスタンスでは、評価と学生のエントリを持つCourseRatingsの配列が1つだけあります。基本的に、コースの部分的なリストを持つ学生の部分的なリストは、学生の完全なリストを持つコースと同様に、データベースの正確な表現ではありません。この時点では、SQLサーバーでどちらが効率的かはわかりませんが、コース数は学生よりも少ないはずです。したがって、コースのすべての学生がコースを希望する場合は、おそらくこの方法のほうが良いでしょう。
JPAを使用する場合は、SQLを確認し、ユースケースを慎重にテストする必要があります。多くの学生がいて、学生ごとにいくつかのコースがあるため、パフォーマンスやメモリのオーバーヘッドは検討に値します。また、fetch
最初にクエリで使用していなかった場合、結果がさらに奇妙になる可能性があることにも注意してください。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加