我有一個 Web 應用程式,用于存盤有關學生、教師、課程和成績的資訊。我在添加/編輯學生成績時遇到了問題。出于某種原因,findById()JPA 存盤庫的內置方法正在回傳一門學生人數翻倍的課程(即使資料庫中該課程顯然只有一個學生)。我附上了我的模型和方法的影像,也是我除錯問題的嘗試。
這是呼叫該方法的地方,我的listStudentsByCourse方法只回傳一個學生串列:

這是listStudentsByCourse方法主體:

這是我的模型:
學生:
@NoArgsConstructor
@Getter
@Setter
@Entity
public class Student {
@Id
private String username;
private String password;
private String name;
private String surname;
@OneToMany(mappedBy = "student", fetch = FetchType.EAGER)
private List<Grade> grades;
public Student(String username, String password, String name, String surname) {
this.username = username;
this.password = password;
this.name = name;
this.surname = surname;
this.grades = new ArrayList<>();
}
public Character getGradeForCourse (Course c) {
return this.grades.stream()
.filter(i -> i.getCourse().getCourseId().equals(c.getCourseId()))
.findFirst().orElse(new Grade()).getGrade();
}
}
課程:
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long courseId;
private String name;
private String description;
@ManyToOne
private Teacher teacher;
@ManyToMany(fetch = FetchType.EAGER)
private List<Student> students;
@Enumerated(value = EnumType.STRING)
private Type type;
public Course(String name, String description, Teacher teacher, Type type) {
this.name = name;
this.description = description;
this.teacher = teacher;
this.type = type;
this.students = new ArrayList<>();
}
}
年級:
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Entity
public class Grade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Character grade;
@ManyToOne
private Course course;
@ManyToOne
private Student student;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime timestamp;
public Grade(Character grade, Course course, Student student, LocalDateTime timestamp) {
this.grade = grade;
this.course = course;
this.student = student;
this.timestamp = timestamp;
}
}
我的資料庫架構:

問題圖片:

這course是回傳的courseRepository.findById()
I have the same student added in one other course and I wonder if they are getting mixed up somehow, but that shouldn't be possible because the course is different (and so is the ID).

This problem is really giving me a hard time and I really don't understand how this is happening. Any info would be helpful, thanks!
Edit 1: I read all of the comments and added them to my code, but the problem persists. I have now noticed that the data doubles when I have one student with a grade already in the course, and if I add another new student without a grade the data doubles not only on the front-end but also in the database.
Here is how I add new students to an existing course, is there anything wrong in the code?:
@Override
public Course addStudentInCourse(String username, Long courseId) {
if (this.studentRepository.findByUsername(username).isEmpty()) {
throw new StudentNotFoundException();
}
if (this.courseRepository.findById(courseId).isEmpty()) {
throw new CourseNotFoundException();
}
Student s = this.studentRepository.findByUsername(username).get();
Course c = this.courseRepository.findById(courseId).get();
if (!c.getStudents().contains(s)){
c.getStudents().add(s);
return this.courseRepository.save(c);
} else {
return c;
}
}
Edit 2: I have come to a breakthrough, sort of.
First of all, here is a picture of my course_student table:

I realized that courseService.findById(courseId), if I have the same student in different courses, it retrieves that student as well (when it shouldn't). For example I have markos in course 5 and course 7.
If I use the following query method to return a list of the students in a certain course, it retrieves the students correctly.
studentRepository.findAllByCourses(c);
Here is a picture from my debugging:

I should probably note that the courses for a Student are Lazy Loaded.

Why is the query method findById returning a Course with students that are not related to that course?
Edit 3: All of the tables and code is the same from edit number 2 (I just kept refreshing the page where I fetch the data). I logged the SQL as many of you suggested. The following SQL queries are from the courseService.findById(courseId) method. I understand the first part, it fetches the course with the correct id (7) from the Course table. Then it selects a student from the course_student table where the id is 5. Where did this 5 come from?
Hibernate:
select
course0_.course_id as course_i1_0_0_,
course0_.description as descript2_0_0_,
course0_.name as name3_0_0_,
course0_.teacher_id as teacher_5_0_0_,
course0_.type as type4_0_0_,
students1_.fk_course as fk_cours1_1_1_,
student2_.username as fk_stude2_1_1_,
student2_.username as username1_3_2_,
student2_.name as name2_3_2_,
student2_.password as password3_3_2_,
student2_.surname as surname4_3_2_,
grades3_.fk_student as fk_stude5_2_3_,
grades3_.id as id1_2_3_,
grades3_.id as id1_2_4_,
grades3_.fk_course as fk_cours4_2_4_,
grades3_.grade as grade2_2_4_,
grades3_.fk_student as fk_stude5_2_4_,
grades3_.timestamp as timestam3_2_4_,
course4_.course_id as course_i1_0_5_,
course4_.description as descript2_0_5_,
course4_.name as name3_0_5_,
course4_.teacher_id as teacher_5_0_5_,
course4_.type as type4_0_5_,
teacher5_.id as id1_4_6_,
teacher5_.date_of_employment as date_of_2_4_6_,
teacher5_.name as name3_4_6_,
teacher5_.surname as surname4_4_6_
from
course course0_
left outer join
course_student students1_
on course0_.course_id=students1_.fk_course
left outer join
student student2_
on students1_.fk_student=student2_.username
left outer join
grade grades3_
on student2_.username=grades3_.fk_student
left outer join
course course4_
on grades3_.fk_course=course4_.course_id
left outer join
teacher teacher5_
on course4_.teacher_id=teacher5_.id
where
course0_.course_id=?
2022-01-06 00:56:38.869 TRACE 15804 --- [nio-8080-exec-4] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [7]
Hibernate:
select
students0_.fk_course as fk_cours1_1_0_,
students0_.fk_student as fk_stude2_1_0_,
student1_.username as username1_3_1_,
student1_.name as name2_3_1_,
student1_.password as password3_3_1_,
student1_.surname as surname4_3_1_
from
course_student students0_
inner join
student student1_
on students0_.fk_student=student1_.username
where
students0_.fk_course=?
2022-01-06 00:56:38.881 TRACE 15804 --- [nio-8080-exec-4] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [5]
Hibernate:
select
course0_.course_id as course_i1_0_0_,
course0_.description as descript2_0_0_,
course0_.name as name3_0_0_,
course0_.teacher_id as teacher_5_0_0_,
course0_.type as type4_0_0_,
students1_.fk_course as fk_cours1_1_1_,
student2_.username as fk_stude2_1_1_,
student2_.username as username1_3_2_,
student2_.name as name2_3_2_,
student2_.password as password3_3_2_,
student2_.surname as surname4_3_2_,
grades3_.fk_student as fk_stude5_2_3_,
grades3_.id as id1_2_3_,
grades3_.id as id1_2_4_,
grades3_.fk_course as fk_cours4_2_4_,
grades3_.grade as grade2_2_4_,
grades3_.fk_student as fk_stude5_2_4_,
grades3_.timestamp as timestam3_2_4_,
course4_.course_id as course_i1_0_5_,
course4_.description as descript2_0_5_,
course4_.name as name3_0_5_,
course4_.teacher_id as teacher_5_0_5_,
course4_.type as type4_0_5_,
teacher5_.id as id1_4_6_,
teacher5_.date_of_employment as date_of_2_4_6_,
teacher5_.name as name3_4_6_,
teacher5_.surname as surname4_4_6_
from
course course0_
left outer join
course_student students1_
on course0_.course_id=students1_.fk_course
left outer join
student student2_
on students1_.fk_student=student2_.username
left outer join
grade grades3_
on student2_.username=grades3_.fk_student
left outer join
course course4_
on grades3_.fk_course=course4_.course_id
left outer join
teacher teacher5_
on course4_.teacher_id=teacher5_.id
where
course0_.course_id=?
2022-01-06 00:56:38.893 TRACE 15804 --- [nio-8080-exec-4] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [7]
Hibernate:
select
students0_.fk_course as fk_cours1_1_0_,
students0_.fk_student as fk_stude2_1_0_,
student1_.username as username1_3_1_,
student1_.name as name2_3_1_,
student1_.password as password3_3_1_,
student1_.surname as surname4_3_1_
from
course_student students0_
inner join
student student1_
on students0_.fk_student=student1_.username
where
students0_.fk_course=?
2022-01-06 00:56:38.909 TRACE 15804 --- [nio-8080-exec-4] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [5]
uj5u.com熱心網友回復:
您的物體 Course 與物體 Student 的關系是多對多。這意味著具有相同 id 的課程行可以容納許多學生。我懷疑當您呼叫findById()生成的 SQL 時,并沒有明確地選擇給定 courseId 的學生。
您的架構中有一個問題,即course_students您的代碼中尚未實作的問題。這是一個嘗試建議,@JoinTable(...)在您的課程物體中添加:
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long course_id;
.
.
// your course fields
.
.
/*** Here you add the many to many table ***/
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "course_students",
joinColumns = {
@JoinColumn(name = "course_course_id", referencedColumnName = "course_id")},
inverseJoinColumns = {
@JoinColumn(name = "students_username", referencedColumnName = "username")})
private List<Student> students;
... //more fields
}
然后在您的學生物體中,從連接表中指定您的映射屬性:
@Entity
public class Student {
@Id
private String username;
.. // student fields
@ManyToMany(mappedBy = "students")
List<Course> courses;
.. //more..
}
uj5u.com熱心網友回復:
謝謝大家的建議,我設法弄明白了。
這是我一直在繪制我的關系的方式。當我將Grade物體中的課程映射為 ManyToOne 時,我忘記Course了使用 OneToMany 注釋映射物體中的成績。這導致 Hibernate 感到困惑,并從兩個表中為課程獲取相同的學生。
這是一個教訓,要始終正確地映射您的關系,不要依靠 Hibernate 來“弄清楚”。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/406481.html
標籤:
