I'm trying to map 2 entities (Course and Student), I have 2 Java classes and 2 MySQL tables, having a ManyToMany relationship. I created the junction table and java class Enrolment (as I want extra information such as the date of enrolment of a student to a course).
I'm trying to insert data using hibernate in this Enrolments table in the MySQL but I keep getting errors. Here are my POJO classes:
Course class:
#Entity
#Table(name = "course")
public class Course {
private int id;
#Column(name = "chapter_id")
private int chapterId;;
#Column(name = "name")
private String title;
#Column(name = "teacher_user_id")
private int teacherId;
#OneToMany(targetEntity=Enrolment.class, mappedBy="course", fetch=FetchType.LAZY)
// #JoinTable(name = "enrolment",
// joinColumns = #JoinColumn(name = "course_id"),
// inverseJoinColumns = #JoinColumn(name = "student_user_id"))
private List<Enrolment> enrolments = new ArrayList<Enrolment>();
public Course(){}
public Course(int id, int chapterId, String title, int teacherId) {
super();
this.id = id;
this.chapterId = chapterId;
this.title = title;
this.teacherId = teacherId;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getChapterId() {
return chapterId;
}
public void setChapterId(int chapterId) {
this.chapterId = chapterId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getTeacherId() {
return teacherId;
}
public void setTeacherId(int teacherId) {
this.teacherId = teacherId;
}
#OneToMany(mappedBy = "course")
public List<Enrolment> getEnrolments() {
return enrolments;
}
public void setEnrolments(List<Enrolment> courses) {
this.enrolments = courses;
}
public void addEnrolment(Enrolment enrolment) {
this.enrolments.add(enrolment);
}
}
Student class (this class is inherited from User parent class, I will attach User Class down below as well. In the database there are different tables as well: User and then Student and Teacher that inherit User parent entity):
#Entity
#Table(name = "student")
#PrimaryKeyJoinColumn(name = "user_id")
#OnDelete(action = OnDeleteAction.CASCADE)
public class Student extends User {
private int grade;
private List<Enrolment> enrolments = new ArrayList<Enrolment>();
public Student(){}
public Student(String fname, String lname, String email, String password, String address, String phone,
int userType, int grade, boolean isAdmin)
{
super(fname, lname, email, password, address, phone, userType, isAdmin);
this.grade=grade;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public void setEnrolments(List<Enrolment> courses) {
this.enrolments = courses;
}
#OneToMany(mappedBy = "student")
public List<Enrolment> getEnrolments() {
return enrolments;
}
public void addCourse(Enrolment course) {
this.enrolments.add(course);
}
public void addEnrolment(Enrolment enrolment) {
this.enrolments.add(enrolment);
}
}
User Class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String firstname;
private String lastname;
private String email;
private String password;
private String address;
private String phone;
#Column(name = "user_type_id")
private int userType;
#Column(name = "is_admin")
private boolean isAdmin;
public User(String fname, String lname, String email, String password, String address, String phone,
int userType, boolean isAdmin) {
//super();
this.firstname = fname;
this.lastname = lname;
this.email = email;
this.password = password;
this.address = address;
this.phone = phone;
this.userType = userType;
this.isAdmin = isAdmin;
}
public User() {}
//getters & setters
And finally this is the Enrolment class:
#Entity
#Table(name = "enrolment")
public class Enrolment {
private int id;
private Student user;
private Course course;
#Column(name = "enrolment_date")
private Date enrolmentDate;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "student_user_id")
public User getUser() {
return user;
}
public void setUser(Student user) {
this.user = user;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "course_id")
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public Date getEnrolmentDate() {
return enrolmentDate;
}
public void setEnrolmentDate(Date enrolmentDate) {
this.enrolmentDate = enrolmentDate;
}
So I'm trying to read a course and a student from database and insert the information in this Enrolment table but it gives errors since trying to read a Course. Here is the DAO method:
#SuppressWarnings("deprecation")
#Transactional
public List<Course> getCoursesOfChapter(int chapterId) {
Configuration con = new Configuration().configure("hibernate.cfg.xml").addAnnotatedClass(Course.class);
SessionFactory sf = con.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from Course where chapter_id = :chapterId");
query.setParameter("chapterId",chapterId);
// List list = query.list();
tx.commit();
return (List<Course>) query.list();
It throws the error at the session factory building:
Exception in thread "main" org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: models.Course.enrolments[models.Enrolment]
at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:1255)
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:808)
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:733)
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:54)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1696)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1664)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:287)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:84)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:474)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:85)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:689)
at dao.CourseDAO.getCourse(CourseDAO.java:52)
at webapp.Main.main(Main.java:132)
Finally, this is my call:
CourseDAO cdao = new CourseDAO();
Course course = cdao.getCourse(1);
I've tried playing with the annotations, make them ManyToMany instead of ManyToOne. I tried to map the User class instead of Student but still didn't work. I tried to make it without the junction class of Enrolment and just generate it without having an actual class for it but still didn't work (as I had to work with 2 session.save() methods one after the other which also gave some error that I couldn't solve). Probably it's a small thing that I'm missing here but I just can't figure it out, sorry for too long code but I really need to solve it fast. If you read through here, I really thank you!
So my question is: Am I missing something here from these mappings and annotations or I should change the structure of my classes?
Boiling down a problem to the bare minimum greatly helps others help you. Here are simpler versions of your student, course and enrollment classes that can be unit tested easily. The many-to-many association between course and student is separated into two many-to-one associations from Enrollment. Note that the associations are bidirectional and that the many side is mapped by the one side. Student cascades persistence operations to Enrollment, which reflects how schools normally work: students enroll in courses, not the other way around.
Course.java
#Entity
public class Course {
#Id
#GeneratedValue
private Long id;
private String title;
#OneToMany(mappedBy = "course")
private List<Enrollment> enrollments;
Course() {
}
Course(String title) {
this.title = title;
this.enrollments = new ArrayList<>();
}
void add(Enrollment enrollment) {
enrollments.add(enrollment);
}
Long getId() {
return id;
}
List<Enrollment> getEnrollments() {
return enrollments;
}
}
Student.java
#Entity
public class Student {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "student", cascade = ALL, orphanRemoval = true)
private List<Enrollment> enrollments;
Student() {
}
Student(String name) {
this.name = name;
this.enrollments = new ArrayList<>();
}
void enroll(Course course) {
enrollments.add(new Enrollment(course, this));
}
}
Enrollment.java
#Entity
public class Enrollment {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private Course course;
#ManyToOne
private Student student;
Enrollment() {
}
Enrollment(Course course, Student student) {
this.course = course;
this.student = student;
course.add(this);
}
}
The test case below checks that the entities are mapped and associated correctly. You can run it with Spring Boot.
SchoolTest.java
#RunWith(SpringRunner.class)
#SpringBootTest
#Transactional
public class SchoolTest {
#Autowired
private CourseRepository courseRepository;
#Autowired
private StudentRepository studentRepository;
#Test
public void run() {
Course course = courseRepository.save(new Course("cs_101"));
int studentCount = 3;
for (int i = 1; i <= studentCount; i++) {
Student student = new Student("student_" + i);
student.enroll(course);
studentRepository.save(student);
}
// push changes to the database and clear the existing entities
// to make the subsequent operations load from the database
entityManager.flush();
entityManager.clear();
Optional<Course> savedCourse = courseRepository.findById(course.getId());
assertTrue(savedCourse.isPresent());
assertEquals(studentCount, savedCourse.get().getEnrollments().size());
}
}
As the warning said, your Enrollment is not registered in Hibernate. If you really don't need it. Please use transient annotation. read more here
Related
I have a problem with my Spring application- am practicing ManyToMany Relationship.
I made two Entity- student, and groups for them- everything is good but when am trying to display them in postman i have spaghetti result like :
{"id":4,"lastName":"guzik","groups":[{"id":1,"groupName":"grupka","students":
....(shorter version, its about 1000 lines)
[{"id":1,"lastName":"smith","groups":[{}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}{"timestamp":"2022-04-25T08:25:31.387+00:00","status":200,"error":"OK","path":"/api/student/4"}
What am doing wrong ?
enter code here
#RestController
#RequestMapping("/api")
public class MainController{
#Autowired
GroupRepo groupRepo;
#Autowired
StudentRepo studentRepo;
#GetMapping("/student/{id}")
ResponseEntity<?> getStudent(#PathVariable long id){
Optional<Students> student=studentRepo.findById(id);
return ResponseEntity.ok().body(student);
}
#GetMapping("/group/{id}")
ResponseEntity<?> getGroup(#PathVariable long id){
Optional<Groupssss> group=groupRepo.findById(id);
return ResponseEntity.ok().body(group);
}
#GetMapping("/student/{id}/groups")
ResponseEntity<?> studentGroups(#PathVariable long id){
Students student=studentRepo.findById(id).orElseThrow(()-> new UsernameNotFoundException("student not found"));
return ResponseEntity.ok().body(student.getGroups());
}
#PostMapping("/add")
#Transactional
ResponseEntity<?> addStudentToGroup(#RequestHeader long id) throws Exception{
Students student=studentRepo.findById(id)
.orElseThrow(()-> new UsernameNotFoundException("student not found"));
Groupssss group=groupRepo.findByGroupName("grupka").orElseThrow(Exception::new);
student.addGroup(group);
studentRepo.save(student);
return ResponseEntity.ok().build();
}
#Entity
public class Groupssss {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String groupName;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "groups")
private Set<Students> students=new HashSet<>();
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setStudents(Set<Students> students) {
this.students = students;
}
public Set<Students> getStudents() {
return students;
}
public String getGroupName() {
return groupName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
#Entity
#Table(name="students")
public class Students {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
String firstName;
String lastName;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name="student_group",
joinColumns = #JoinColumn(name="student_id"),
inverseJoinColumns = #JoinColumn(name="group_id"))
Set<Groupssss> groups=new HashSet<>();
public Students(){}
public Students(String firstName, String lastName){
this.firstName=firstName;
this.lastName=lastName;
}
public void addGroup(Groupssss group){
this.groups.add(group);
group.getStudents().add(this);
}
public Set<Groupssss> getGroups() {
return groups;
}
public String getLastName() {
return lastName;
}
public String getName() {
return firstName;
}
public void setGroups(Set<Groupssss> groups) {
this.groups = groups;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setName(String firstName) {
this.firstName = firstName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
If you use ManyToMany relationship, It calls the Parent and the Child entity recursively when you try to get List of it. There must be ignorance in child entity like:
In Parent entity you could call #JsonManagedReference
In Child entity you could call #JsonBackReference
Documentation is here:
Bidirectional Relationships
public class User {
public int id;
public String name;
#JsonManagedReference
public List<Item> userItems;
}
public class Item {
public int id;
public String itemName;
#JsonBackReference
public User owner;
}
Or you can use #JsonIgnore on top of the Parent declaration in Child entity. Some of the case it gets worst when you use JsonIgnore.
Documentation is here:
Jackson Ignore Properties
public class MyDto {
private String stringValue;
#JsonIgnore
private int intValue;
private boolean booleanValue;
public MyDto() {
super();
}
}
Or use #JsonIgnoreProperties when you need to ignore many fields
#JsonIgnoreProperties(value = { "intValue" })
public class MyDto {
private String stringValue;
private int intValue;
private boolean booleanValue;
public MyDto() {
super();
}
}
!Enjoy
I'm beginner at hibernate and try to learn eager and lazy loading
I know that if the hibernate session is closed and then I try to retrieve lazy data, then Hibernate will throw an exception.
Now, I've tried to load main entity(Instructor) in first session and then load dependent entity(Course) in a new separate session:
Main Class(Test Class)
public class EagerLazyDemo {
public static void main(String[] args) {
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml")
.addAnnotatedClass(Instructor.class)
.addAnnotatedClass(InstructorDetail.class)
.addAnnotatedClass(Course.class)
.buildSessionFactory();
Session session = factory.getCurrentSession();
// Begin A Transaction
session.beginTransaction();
// Get The Instructor From DB
Instructor theInstructor = session.get(Instructor.class, 1);
// Commit Transaction
session.getTransaction().commit();
session.close();
//------
//new session
session = factory.getCurrentSession();
session.beginTransaction();
//Get And Display Courses For The Instructor
List<Course> courses = theInstructor.getCourses();
printCourses(courses);
session.getTransaction().commit();
session.close();
}
private static void printCourses(List<Course> courses) {
for (Course c : courses) {
System.out.println(c);
}
}
}
but hibernate throw this exception:
Error:
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mehdisarf.hibernate.demo.entity.Instructor.courses, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:387)
at com.mehdisarf.hibernate.demo.EagerLazyDemo.printCourses(EagerLazyDemo.java:55)
at com.mehdisarf.hibernate.demo.EagerLazyDemo.main(EagerLazyDemo.java:42)
it says:
could not initialize proxy - no Session
Although I have a session for loading dependent entity
These Are My Entity Classes:
Instructor Class (main entity)
#Entity
#Table(name = "instructor")
public class Instructor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "instructor_detail_id")
private InstructorDetail instructorDetail;
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "instructor",
cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
private List<Course> courses;
public Instructor() {
}
public Instructor(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public void addCourse(Course theCourse) {
if (courses == null) {
courses = new ArrayList<>();
}
this.courses.add(theCourse);
theCourse.setInstructor(this);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public InstructorDetail getInstructorDetail() {
return instructorDetail;
}
public void setInstructorDetail(InstructorDetail instructorDetail) {
this.instructorDetail = instructorDetail;
}
public List<Course> getCourses() {
return courses;
}
public void setCourses(List<Course> courses) {
this.courses = courses;
}
#Override
public String toString() {
return id + "" + firstName + "" + lastName + "" + email + " (Detail:" + instructorDetail+")";
}
}
Course Class (dependent entity)
#Entity
#Table(name = "course")
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "title")
private String title;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name = "instructor_id")
private Instructor instructor;
public Course() {
}
public Course(String title) {
this.title = title;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Instructor getInstructor() {
return instructor;
}
public void setInstructor(Instructor instructor) {
this.instructor = instructor;
}
#Override
public String toString() {
return id + "|" + title + "|";
}
}
Your object theInstructor is still linked to the former session. In order to retrieve your object you have to make another call after opening the new session :
theInstructor = session.get(Instructor.class, 1);
"theInstructor" is not attached to any session while "theInstructor.getCourses()" call is made, this results in exception.
When object is returned by the get method it is in the Persistent state.
Any instance returned by a get() or load() method is persistent
Session Documentation
When session is closed "theInstructor" object goes into the Detached state.
Detached - a detached instance is an object that has been persistent, but its Session has been closed. Hibernate object states documentation
Call made to session = factory.getCurrentSession(); returns new session object, but theInstructor object is not attached to the new session. Hence an attempt to lazily load the courses fails.
So I am having a little trouble trying to get my create user function to work. I am trying to create a new user for every new user entry from the Scanner console, but my Hibernate is updating the same person ID, instead of assigning a new person ID to the new user.
my DAO class:
public void createDoctor(Doctor doctor) {
sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(doctor);
session.getTransaction().commit();
session.close();
sessionFactory.close();
}
my main class:
int adminNum = sc.nextInt();
if (adminNum == 1) {
System.out.print("Please enter doctor's first name: ");
sc.nextLine();
String firstName = sc.nextLine();
System.out.print("Please enter doctor's last name: ");
String lastName = sc.nextLine();
Doctor d = new Doctor();
d.setFirstName(firstName);
d.setLastName(lastName);
doctorList.add(d);
int index = doctorList.indexOf(d);
dao.createDoctor(doctorList.get(index));
}
Doctor Class:
import java.util.*;
import javax.persistence.*;
#Entity
public class Doctor extends Person {
#OneToOne
#JoinColumn(name = "SPECIALTY_ID")
private Specialty specialty;
#OneToMany(mappedBy = "doctor", targetEntity = Patient.class,fetch=FetchType.EAGER, cascade= CascadeType.ALL )
private List<Patient> patients;
private double salary;
public Doctor(){
patients = new ArrayList<Patient>();
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSpecialty(Specialty specialty) {
this.specialty = specialty;
}
public Specialty getspecialty() {
return specialty;
}
public void setPatient(Patient patient){
patients.add(patient);
}
public List<Patient> getPatients(){
return patients;
}
}
Person Class:
package edu.cs157b.medicalSystem;
import javax.persistence.*;
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "PERSON_ID")
private int personId;
private String first_name;
private String last_name;
private char sex;
public Person() {
}
public int getPersonId() {
return personId;
}
public void setPersonId(int personId) {
this.personId = personId;
}
public void setFirstName(String first_name) {
this.first_name = first_name;
}
public String getFirstName() {
return first_name;
}
public void setLastName(String last_name) {
this.last_name = last_name;
}
public String getLastName() {
return last_name;
}
public void setSex(char sex){
this.sex = sex;
}
public char getSex(){
return sex;
}
}
In your Person class declare the id field as Integer so that newly created instances will have null value as personId. The primitive int type will always have 0 as initial value.
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "PERSON_ID")
private Integer personId;
I don't like this code much.
Personally, I'd prefer having a ctor that did something rather than a do-nothing default ctor and having to call setters.
I'd persist the Doctor, then add it to the List.
Is this synchronized? Doesn't look thread safe if that List is shared, mutable data.
I see no equals or hashCode implemented here. That might explain it. Best to use the primary key fields to determine equality.
I think you're desperately in need of a tutorial. Forget your assignment for a moment and do something simple:
http://docs.jboss.org/hibernate/core/3.3/reference/en-US/html/tutorial.html
I'm trying to create manytomany realation between Student and Teaching Course using Composite Primary key:
my classes:
#Entity
#Table(name="Student_mtm_cId")
public class Student {
private String id;
private Set<StudentTClass> teachingClasses = new HashSet<StudentTClass>();
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.student")
public Set<StudentTClass> getTeachingClasses() {
return teachingClasses;
}
public void setTeachingClasses(Set<StudentTClass> teachingClasses) {
this.teachingClasses = teachingClasses;
}
public void addStudentToClass(TeachingClass teachingClass){
StudentTClass studentTClass = new StudentTClass();
studentTClass.setStudent(this);
studentTClass.setTeachingClass(teachingClass);
teachingClasses.add(studentTClass);
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Id #GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
#Column(name = "student_id", nullable = false)
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
//all other setters and getters and isequal/hashCode omitted.
}
TeachingClass:
#Entity
#Table(name="TechingClass_MTM")
public class TeachingClass {
private String id;
private String name;
private String description;
private Set<StudentTClass> teachingClasses = new HashSet<StudentTClass>();
public TeachingClass(){}
public TeachingClass(String name, String description) {
super();
this.name = name;
this.description = description;
}
public void addStudentToClass(Student student){
StudentTClass studentTClass = new StudentTClass();
studentTClass.setStudent(student);
studentTClass.setTeachingClass(this);
teachingClasses.add(studentTClass);
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.teachingClass")
public Set<StudentTClass> getTeachingClasses() {
return teachingClasses;
}
public void setTeachingClasses(Set<StudentTClass> teachingClasses) {
this.teachingClasses = teachingClasses;
}
public void setDescription(String description) {
this.description = description;
}
#Id #GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
#Column(name = "teachingClass_id", nullable = false)
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Collection Objects:
#Entity
#Table(name = "student_TClass_MTM")
#AssociationOverrides({
#AssociationOverride(name = "pk.student", joinColumns = #JoinColumn(name = "student_id")),
#AssociationOverride(name = "pk.teachingClass", joinColumns = #JoinColumn(name = "teachingClass_id"))
})
public class StudentTClass {
#EmbeddedId
private StudentTClassPK pk = new StudentTClassPK();
public StudentTClassPK getPk() {
return pk;
}
public void setPk(StudentTClassPK pk) {
this.pk = pk;
}
public StudentTClass() {}
#Transient
public Student getStudent(){
return this.pk.getStudent();
}
#Transient
public TeachingClass getTeachingClass(){
return this.pk.getTeachingClass();
}
public void setStudent(Student student){
this.pk.setStudent(student);
}
public void setTeachingClass(TeachingClass teachingClass){
this.pk.setTeachingClass(teachingClass);
}
}
Now The primary Key:
#Embeddable
public class StudentTClassPK implements Serializable{
private static final long serialVersionUID = -7261887879839337877L;
private Student student;
private TeachingClass teachingClass;
#ManyToOne
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
#ManyToOne
public TeachingClass getTeachingClass() {
return teachingClass;
}
public void setTeachingClass(TeachingClass teachingClass) {
this.teachingClass = teachingClass;
}
public StudentTClassPK(Student student, TeachingClass teachingClass) {
this.student = student;
this.teachingClass = teachingClass;
}
public StudentTClassPK() {}
}
When I'm trying to Persist Student I got the following error:
Caused by: org.hibernate.MappingException: Could not determine type for: com.vanilla.objects.Student, at table: student_TClass_MTM, for columns: [org.hibernate.mapping.Column(student)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:306)
at org.hibernate.tuple.PropertyFactory.buildStandardProperty(PropertyFactory.java:143)
at org.hibernate.tuple.component.ComponentMetamodel.<init>(ComponentMetamodel.java:68)
at org.hibernate.mapping.Component.buildType(Component.java:184)
at org.hibernate.mapping.Component.getType(Component.java:177)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:290)
at org.hibernate.mapping.RootClass.validate(RootClass.java:236)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1362)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1865)
at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:855)
at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:774)
at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:211)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
... 51 more
What am I doing wrong?
I solved this issue. I mapped Getter instead of field.
public class StudentTClass {
//#EmbeddedId
private StudentTClassPK pk = new StudentTClassPK();
#EmbeddedId
public StudentTClassPK getPk() {
return pk;
}
If you can, I'd seriously suggest removing the composite keys. Worth with simple primary keys can both make a lot of problems go away and simplify your code. I have used composite keys in a database in the past because I had no ability to modify the db. Unfortunately I don't have the code. But I do remember it took some work to get it all working correctly. Sorry, can't help more.
Okay I have an application that maps Semesters to their courses;
public class Course {
private String courseId;
private String courseName;
private Collection<Semester> semesters = new ArrayList<>();
#OneToMany(targetEntity = Semester.class, mappedBy = "course")
public Collection<Semester> getSemesters() {
return semesters;
}
public void setSemesters(Collection<Semester> semesters) {
this.semesters = semesters;
}
#Id
#Column (name = "COURSE_ID")
public String getCourseId() {
return courseId;
}
public void setCourseId(String courseId) {
this.courseId = courseId;
}
#Column(name = "COURSE_NAME", nullable = false, unique = true)
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
}
As you can see the users class is mapped to the Semesters entity using One to Many mapping.
The Semester class is as follows;
#Entity
#Table (name = "SEMESTERS")
public class Semester {
private int semNum;
private Course course;
#ManyToOne
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
#Id
#Column (name = "SEM_NUM")
public int getSemNum() {
return semNum;
}
public void setSemNum(int semNum) {
this.semNum = semNum;
}
}
As you can see I am using mappedBy to map the course directly into the semester table. But the problem is that the field in the semester table comes as course_COURSE_ID.
How can I change this column name to just COURSE_ID ?
You need to use the #JoinColumn annotation.
#ManyToOne
#JoinColumn(name="COURSE_ID")
public Course getCourse() {
//code
}
I would suggest you to read the documentation as this is very well explained in there. If you don't want to read the docs, then be ready for a long and painful journey, as JPA is not a simple technology, and it has a multitude of gotchas.
You can change this via #JoinColumn in the Semester class. This allow to change the default JPA names.
Should look like this:
public class Semester {
#ManyToOne
#JoinColumn(name="COURSE_ID")
public Course getCourse() {
return course;
}
}