java JPQL:使用复合键建模具有多个关系的查询实体
我读了this article关于如何使用复合键构建多对多。 这些实体是
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
}
现在,让我们假设,我想让学生有特定的课程
那么我的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
或者完全不同
# 1 楼答案
在这类JPA查询中,您应该仔细检查SQL,因为那里发生的事情可能比您意识到的要多
您在设置
ManyToMany
与属性rating
的关系方面做得很好,您的第二个查询可以工作,但很容易导致问题和可能的性能考虑为了只获取学生,正确的查询可以是
这就是日志
JPA将生成一个额外的查询来获取课程信息,因为它不是预取的。你可以通过自己预取来解决这个问题
这就是日志
有一个问题是,你的学生只有部分课程评分。如果你试图更新一个学生的评分,我认为你会导致其他课程评分丢失。也许对你的用例来说不是问题,但你也可以通过学生名单获得课程:
这将在一个稍微不同的查询中得到相同的结果,你可以在不影响其他课程的情况下更新学生的评分
如果您正在创建REST服务,这也会影响JSON格式。在第一种情况下,你有一个条目的多个课程表数组,在第二种情况下,你只有一个评分和学生条目的课程表数组。基本上,部分学生列表和部分课程列表并不能准确地表示数据库,而作为一门完整的学生列表的课程,数据库是完整的。我现在不确定哪一个在SQL server上更有效,但课程应该比学生少很多,所以如果你想让所有学生都上一门课,这样可能更好
使用JPA,您应该仔细检查SQL并测试用例。由于学生人数众多,每个学生都有几门课程,因此性能或内存开销可能值得考虑。还要注意的是,如果在查询开始时没有使用
fetch
,结果可能会变得更加奇怪# 2 楼答案
你也可以把Student对象放在你的可嵌入id中,以防你不需要它的id。这样你就更容易阅读你的代码,而且你不必让
@Column(name = "student_id") Long studentId
成为不可插入和不可更新的