I am new to Spring data and the world of JPA.
I have the following entities:
Student:
@Entity
@Table(name = "STUDENT")
public class Student {
@Id
@GeneratedValue
private Integer studentId;
@Column
private boolean fullTime;
@Column
private Integer age;
@Embedded
private Person attendee;
@ManyToMany(mappedBy = "students", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Course> courses = new ArrayList<>();
public Student(Person attendee, boolean fullTime, Integer age) {
this.attendee = attendee;
this.fullTime = fullTime;
this.age = age;
courses = new ArrayList<>();
}
//getter and setters here
}
Course:
@Entity
@Table(name = "COURSE")
public class Course {
@Id
@GeneratedValue
private Integer id;
@Column
private String name;
@ManyToOne
@JoinColumn
private Department department;
@ManyToMany
@JoinTable
List<Student> students;
public Course(String name, Department department) {
this.name = name;
this.department = department;
}
//getters and setters
}
Department:
@Entity
@Table(name = "Department")
public class Department {
@Id
@GeneratedValue
private Integer id;
@Column
private String name;
@OneToMany(mappedBy = "department", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Course> courses = new ArrayList<>();
public Department(String name) {
this.name = name;
}
//getters and setters
}
I have the following test case which explores the springs data's basic functionality:
@RunWith(SpringRunner.class)
@SpringBootTest
public class CrudRepositoryDemo {
Department department;
Course course;
Course course1;
@Autowired
StudentRepository studentRepository;
@Autowired
CourseRepository courseRepository;
@Autowired
DepartmentRepository departmentRepository;
/**
* Exercise CrudRepository methods.
*/
@Test
public void simpleStudentCrudExample() {
boolean fullTime = true;
studentRepository.save(new Student(new Person("jane", "doe"), fullTime, 20));
studentRepository.save(new Student(new Person("john", "doe"), fullTime, 22));
studentRepository.save(new Student(new Person("mike", "smith"), fullTime, 18));
studentRepository.save(new Student(new Person("ally", "kim"), !fullTime, 19));
createDepartment();
createCourse();
System.out.println("\n*************Original Students*************");
studentRepository.findAll().forEach(System.out::println);
System.out.println("\n*************Printing courses*************");
List<Course> all = courseRepository.findAll();
all.forEach(System.out::println);
//age up the students and add course
studentRepository.findAll().forEach(student -> {
student.setAge(student.getAge() 1);
student.getCourses().addAll(all);
Student save = studentRepository.save(student);
System.out.println(save);
});
System.out.println("\n*************Students a year older and course added*************");
for (Student student : studentRepository.findAll()) {
System.out.println("Name of student = " student.getAttendee().getFirstName() ". Course = " student.getCourses());
}
studentRepository.deleteAll();
}
private void createCourse() {
course = new Course("Algorithm", department);
course1 = new Course("Databases", department);
courseRepository.save(course);
courseRepository.save(course1);
}
private void createDepartment() {
department = new Department("CompSci");
departmentRepository.save(department);
System.out.println("\n*************The following department has been saved*************");
departmentRepository.findAll().forEach(System.out::println);
}
}
Now when i run the above test, it compiles and executes BUT for some reason only the last student has a course associated to it based on the output. I can see from the logs that the students do have the course saved to them but when i view all the students, only one student has the courses associated to it and the other students have no courses.
I'm suspecting it's because I'm using a @OneToMany
from the Student to Courses.. but to me it makes sense for it to be a unidirectional OneToMany (in this context) since A student can have MULTIPLE courses. I'm not to concenered about the courses knowing anything to do with the students hence it's unidirectional.
So my overall question is why don't the other students have the courses saved to them? Why is it only one student?
Edit = This is the output after using Many to Many (I have updated the students and courses entites too. I updated the Student's toString method so it's easier to see whats going on in the logs too.):
*************The following department has been saved*************
2021-12-11 20:56:25.811 INFO 11980 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Department{id=5, name='CompSci', courses=[]}
*************Original Students*************
Student{studentId=1, firstName='jane', lastname='doe' , fullTime=true, age=20 , course=}
Student{studentId=2, firstName='john', lastname='doe' , fullTime=true, age=22 , course=}
Student{studentId=3, firstName='mike', lastname='smith' , fullTime=true, age=18 , course=}
Student{studentId=4, firstName='ally', lastname='kim' , fullTime=false, age=19 , course=}
*************Printing courses*************
Course{id=6, name='Algorithm', department=CompSci}
Course{id=7, name='Databases', department=CompSci}
Student{studentId=1, firstName='jane', lastname='doe' , fullTime=true, age=21 , course=AlgorithmDatabases}
Student{studentId=2, firstName='john', lastname='doe' , fullTime=true, age=23 , course=AlgorithmDatabases}
Student{studentId=3, firstName='mike', lastname='smith' , fullTime=true, age=19 , course=AlgorithmDatabases}
Student{studentId=4, firstName='ally', lastname='kim' , fullTime=false, age=20 , course=AlgorithmDatabases}
*************Students a year older and course added*************
Student{studentId=1, firstName='jane', lastname='doe' , fullTime=true, age=21 , course=}
Student{studentId=2, firstName='john', lastname='doe' , fullTime=true, age=23 , course=}
Student{studentId=3, firstName='mike', lastname='smith' , fullTime=true, age=19 , course=}
Student{studentId=4, firstName='ally', lastname='kim' , fullTime=false, age=20 , course=}
CodePudding user response:
You need to understand what @OneToMany
means and why you have misused it in your case.
@Entity
@Table(name = "STUDENT")
public class Student {
....
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn//(name = "id")
private List<Course> courses = new ArrayList<>();
...
@OneToMany
means that 1 student can hold reference to many courses. Up to here this is good for you.
Bot you are forced on the other side (Course.class) to use @ManyToOne
which means that many courses are related only with ONE student.
Your structure would require a relationship both on Student.class
of @ManyToMany
and also on Course.class
of @ManyToMany
.
This would then correctly mean when you check from side of Course.class
that many courses can be related with many students at the same time!
CodePudding user response:
It's because the default FetchType in ManyToMany relation is set to LAZY. If you want your courses you need to implement a query fetching also the courses or set the FetchType to EAGER ( bad practice ).
Try EntityGraph.