I have two classes with ManyToMany relationship.
Student.java
#Entity
public class Student {
#Id
private Long id;
private String name;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "student_class", joinColumns = {#JoinColumn(name = "student_id")},
inverseJoinColumns = {#JoinColumn(name = "class_id")})
private List<Class> classList = new ArrayList<>();
}
Class.java
#Entity
public class Class {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToMany(mappedBy = "classList",cascade = {CascadeType.ALL})
private List<Student> students;
}
My question is how to implement a POST request in the controller? Do I add class to classList for a student and the student will automatically get inserted into students list?
The class that will actually be monitored by JPA for references is the one pointed with mappedBy .
This means that JPA during a save or update or delete will consider the mappings that the Student holds on classList. Therefore the cascade you have written here has no point.
#ManyToMany(mappedBy = "classList",cascade = {CascadeType.ALL})
private List<Student> students;
The only cascade that will actualy work is the one pointed with mappedBy
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "student_class", joinColumns = {#JoinColumn(name = "student_id")},
inverseJoinColumns = {#JoinColumn(name = "class_id")})
private List<Class> classList = new ArrayList<>();
So to your question
Do I add class to classList for a student and the student will
automatically get inserted into students list?
Yes it will be added to the list so you can see it, but it will not consider any changes that you make to that studentList. So if you make modifications directly on studentList then those modifications will not be persisted in your database.
Related
I have two entities connected with many-to-many relationship. For example:
#Entity
public class Account {
#Id
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "account_games",
joinColumns = {#JoinColumn(name="account_id")},
inverseJoinColumns = {#JoinColumn(name="game_id")}
)
private Set<Game> games = new HashSet<>();
}
#Entity
public class Game {
#Id
private Long id;
#ManyToMany(mappedBy = "games", fetch = FetchType.LAZY)
List<Account> accounts = new ArrayList<>();
}
So, there is a table account_games(account_id, game_id) in mysql describing entities many-to-many relations.
I don't want to have Game entity anymore. Is there a way to get rid of Game and leave gameId relation only? So, I'd like to have code something like that:
#Entity
public class Account {
#Id
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "account_games",
joinColumns = {#JoinColumn(name="account_id")},
inverseJoinColumns = {#JoinColumn(name="game_id")}
)
private Set<Long> gameIds = new HashSet<>();
}
without making changes in database.
I've tried different configuration on javax.persistance annotations, but none worked
You can use #ElementCollection and #CollectionTable to achieve that.
#Entity
public class Account {
#Id
private Long id;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "account_games", joinColumns = #JoinColumn(name = "account_id"))
#Column(name = "game_id", nullable = false)
private Set<Long> gameIds = new HashSet<>();
}
You may have to change the query on how to filter data using gameId. Element Collection Query
I have this many-many relationship between students and courses as in following JPA models
#Entity(name = "course_result")
public class CourseResult {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private BigInteger id;
#ManyToOne(targetEntity = Student.class, cascade = CascadeType.ALL)
#JoinColumn(name = "student_id")
private Student student;
#ManyToOne(targetEntity = Course.class, cascade = CascadeType.ALL)
#JoinColumn(name = "course_id", referencedColumnName = "id")
private Course course;
private float grade;
}
#Entity
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private BigInteger id;
private String courseName;
private String description;
#OneToMany(mappedBy = "course", fetch = FetchType.EAGER)
private Set<CourseResult> courseResult;
}
#Entity
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private BigInteger id;
private String studentName;
private String studentClass;
#OneToMany(mappedBy = "student", fetch = FetchType.EAGER)
private Set<CourseResult> courseResult;
}
When persisting a CourseResult, I want a new course/student would be created if it not yet exists otherwise they'll be updated only. That's why I set the cascade type to ALL. T
However, it always throws an exception when I try to persist a new CourseResult with existing Student and Course, say both with Id = 1
Student student = studentRepo.findById(BigInteger.ONE).get();
Course course = courseRepo.findById(BigInteger.ONE).get();
CourseResult courseResult = new CourseResult()
.setCourse(course)
.setStudent(student)
.setGrade(1.5F);
courseResultRepository.save(courseResult);
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: org.example.domain.Course; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: org.example.domain.Course
Do you have any idea why?
TIA,
There are a couple of approaches you could try
Is there a Transaction around your Code ? It is not shown in your example. Ensure that the import is using the spring Transaction.
Try adding the courseResult to the other enities
Student student = studentRepo.findById(BigInteger.ONE).get();
Course course = courseRepo.findById(BigInteger.ONE).get();
student.getCourseResult().add()
CourseResult courseResult = new CourseResult()
.setGrade(1.5F);
student.getCourseResult().add(courseResult);
course.getCourseResult().add(courseResult);
Try saving the CourseResult before adding it to the Objects. That way it might then be recognized by Hibernate as a managed entity
Student student = studentRepo.findById(BigInteger.ONE).get();
Course course = courseRepo.findById(BigInteger.ONE).get();
student.getCourseResult().add()
CourseResult courseResult = new CourseResult()
.setGrade(1.5F);
courseResultRepository.save(courseResult);
courseResult.setStudent(student);
courseResult.setCourse(course);
courseResultRepository.save(courseResult);
Try adding
cascade = CascadeType.ALL
to
#OneToMany(mappedBy = "course", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<CourseResult> courseResult;
and
#OneToMany(mappedBy = "student", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<CourseResult> courseResult;
Try using cascade= CascadeTyp.Mergeinsdead of cascade= CascadeTyp.ALL (PersistentObjectException: detached entity passed to persist thrown by JPA and Hibernate)
I have a Parent class Seller , it have onetomany mappings to all many child classes. It also has some properties as name,id , email . In edit sequence I am fetching this object displays it on jsp , bind it through Spring MVC form , takes input from user and then save it as it is to db using session.saveorupdate(seller).
But while doing so my previous mappings with all the child classes are getting deleted. Only the mapping from join tables are getting deleted. If there is a two way mapping using mappedBy , it is not deleted.
All the mapping are lazy fetch.
Scenario is :
Step 1: Register seller . Add email ,name values.
Step 2 : Create child object in different journeys , create List of A ,Band add to existing seller object.
Step 3: Change name of seller , editing the seller and after saving all the previous mapping are gone.
Seller.java
#Entity
#Table(name = "Seller")
public class Seller {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
private String name;
#Column
private String address;
#Column
private String email;
#OneToOne(cascade = CascadeType.ALL)
#JoinTable(name = "seller_roles", joinColumns = { #JoinColumn(name = "seller_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "role_id", referencedColumnName = "id") })
private Role role;
#OneToMany(mappedBy = "seller", cascade = CascadeType.ALL)
private List<A> a = new ArrayList<A>();
#OneToMany(cascade = CascadeType.ALL)
private List<B> b = new ArrayList<B>();
#OneToMany(mappedBy = "seller", cascade = CascadeType.ALL)
private List<C> c = new ArrayList<C>();
#OneToMany(cascade = CascadeType.ALL)
private List<D> d = new ArrayList<D>();
#OneToMany(cascade = CascadeType.ALL)
private List<E> e = new ArrayList<E>();
Saving seller in SellerDaoImpl as :
Session session = sessionFactory.openSession();
session.beginTransaction();
session.saveOrUpdate(seller);
session.getTransaction().commit();
session.close();
Please help.
I have two classes Student and Subject:
Student
#Entity
#Table(name = "students")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "full_name")
private String fullName;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "students")
private Set<Subject> subject;
//Getters & setters
}
Subject
#Entity
#Table(name = "subjects")
public class Subject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "subject_name")
private String name;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Student> students;
//Getters & setters
}
When I am trying to save a student registered with more than one subject, I am not getting any records in the JoinTable.
My code for saving the entiers:
Subject subject = new Subject();
subject.setName("JAVA");
Subject subject2 = new Subject();
subject2.setName("C");
Subject subject3 = new Subject();
subject3.setName("C++");
Student student = new Student();
student.setFullName("dibya");
Set<Subject> subjects = new HashSet<>();
subjects.add(subject2);
subjects.add(subject);
subjects.add(subject3);
student.setSubject(subjects);
session.saveOrUpdate(student);
Please tell me where am I doing wrong.
Found the solution to my problem.
It was not with the configuration. With just mappedBy attribute ManyToMany relationship can be accomplished.
In my program I have added session.saveOrUpdate(subject); after saving saving the students.
This solved my problem. :)
A many-to-many relationship is modeled as a join table. This is a table with 2 columns, both are foreign keys to the tables of the repationship (Student and Subject in your case).
In Hibernate if you use #ManyToMany you need to either #JoinTable or #JoinColumn to define the relationship. Once it's done your update will work.
See
http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch07.html
and
http://www.dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html
EDIT:
Applied to your code it should look like (UNTESTED):
In Student:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "student_id")
private int id;
....
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "students_subject", joinColumns = { #JoinColumn(name = "student_id") }, inverseJoinColumns = { #JoinColumn(name = "subject_id") })
private Set<Subject> subject;
In Subject
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "subject_id")
private int id;
...
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "students_subject", joinColumns = { #JoinColumn(name = "subject_id") }, inverseJoinColumns = { #JoinColumn(name = "student_id") })
private Set<Student> students;
Your case might be falling into mixing JPA and Hibernate cascading. Try annotate your #ManyToMany with additional
#Cascade({CascadeType.SAVE_UPDATE})
you can also consult this two links to check if this is really your case:
Confusion between JPA and Hibernate cascading
http://www.mkyong.com/hibernate/cascade-jpa-hibernate-annotation-common-mistake/
I have an existing database modeled the following way:
Student - SchoolId(PK), StudentId(PK), StudentName
Teacher - SchoolId(PK), TeacherId(PK), TeacherName
Student_Teacher - SchoolId(PK), StudentId(PK), TeacherId(PK)
Foreign key references exist from Student_Teacher to respective entities.
Now I am creating hibernate entities for this existing database. And I am running into weird issues creating Many-to-Many mapping from Student to Teacher.
#Entity
#Table(name = "Student")
public class Student {
#EmbeddableId
private StudentPK itemId;
#Column(name="StudentName")
private String studentName;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="Student_Teacher", joinColumns={#JoinColumn(name="SchoolId", referencedColumnName="SchoolId"),#JoinColumn(name="StudentId", referencedColumnName="StudentId")}, inverseJoinColumns={#JoinColumn(name="SchoolId", referencedColumnName="SchoolId"),#JoinColumn(name="TeacherId", referencedColumnName="TeacherId")})
private List<Teacher> attachments=new ArrayList<Teacher>();
}
The above code compains about some duplicate SchoolId reference.
Any ideas?
As I see that there is an issue in your mapping of entities, It should be as follows
school - school_id(PK), school_name
student - student_id(PK) , student_name, fk_school_id(FK),
teacher - teacher_id(PK), teacher_name , fk_school_id(FK)
*student_teacher* - student_teacher_id(PK), fk_student_id(FK), fk_teacher_id(FK)
and Entity clasess as follows
School Entity
#Entity
#Table(name = "school")
public class School {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column (name = "school_id")
private int Id;
#Column(name="school_name")
private String schoolName;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "school")
private Set<Student> students = new HashSet<Student>
#OneToMany(fetch = FetchType.LAZY, mappedBy = "school")
private Set<Teacher> teachers = new HashSet<Teacher>
}
Student Entity
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column (name = "student_id")
private int Id;
#Column(name="student_name")
private String studentName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "school_id", nullable = false)
private School school;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "student_teacher", joinColumns = {#JoinColumn(name = "fk_student_id") }, inverseJoinColumns = { #JoinColumn(name = "fk_teacher_id") })
private List<Teacher> teachers = new ArrayList<Teacher>();
}
Teacher Entity
#Entity
#Table(name = "teacher")
public class Teacher {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column (name = "teacher_id")
private int Id;
#Column(name="teacher_name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "school_id", nullable = false)
private School school;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "student_teacher", joinColumns = {#JoinColumn(name = "fk_teacher_id") }, inverseJoinColumns = { #JoinColumn(name = "fk_student_id") })
private List<Student> students =new ArrayList<Student>();
}
hope this will solve this problem..
as you have declare 'SchoolId' as PK in Student_Teacher table it will not allow you to add duplicate entry for SchoolId field for Student_Teacher table and this is not the case. thus the above relationship will gives duplicate SchoolId reference. when you are going to add two different students from same school into Student_Teacher table..
Did you define the various PKs per entity as compound keys, as i note that you use multiple PKs per entity. Is there any constraint why you can't use a sole PK per entity and just use a relation table to bind the 2 entities?