I'm trying to update 2 tables with many-to-many relationship:
I have 2 class:
Supplier.java:
#Entity
#Table(name = "Suppliers")
public class Supplier implements Serializable {
#Id
String id;
String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "Suppliers_Categories", joinColumns = { #JoinColumn(name = "SupplierId") }, inverseJoinColumns = { #JoinColumn(name = "CategoryId") })
Set<Category> categories = new HashSet<Category>();
#OneToMany(mappedBy = "supplier")
Collection<Product> products;
public Set<Category> getCategories() {
return this.categories;
}
public void setCategories(Set<Category> categories) {
this.categories = categories;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<Product> getProducts() {
return products;
}
public void setProducts(Collection<Product> products) {
this.products = products;
}
}
Category.java:
#Entity
#Table(name = "Categories")
public class Category implements Serializable {
#Id
#GeneratedValue
Integer id;
String name;
String namevn;
#ManyToMany(mappedBy = "categories")
Set<Supplier> suppliers = new HashSet<Supplier>(0);
#OneToMany(mappedBy = "category")
Collection<Product> products;
#OneToOne
#JoinColumn(name = "ProductFeature")
Product featureProduct;
public Set<Supplier> getSuppliers() {
return this.suppliers;
}
public Product getFeatureProduct() {
return featureProduct;
}
public void setFeatureProduct(Product featureProduct) {
this.featureProduct = featureProduct;
}
public String getNamevn() {
return namevn;
}
public void setNamevn(String namevn) {
this.namevn = namevn;
}
public void setSuppliers(Set<Supplier> suppliers) {
this.suppliers = suppliers;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<Product> getProducts() {
return products;
}
public void setProducts(Collection<Product> products) {
this.products = products;
}
}
My code for update the relation ship is:
public class CategoryController extends ActionSupport implements
ModelDriven<Category> {
Category model = new Category();
public Category getModel() {
return model;
}
public void setModel(Category model) {
this.model = model;
}
#Action("/admin/category/update")
public String update() {
try{
Supplier s = XHibernate.load(Supplier.class, "1");
if (!model.getSuppliers().contains(s)) {
model.getSuppliers().add(s);
s.getCategories().add(model);
}
Session session = XHibernate.openSession();
Transaction transaction = session.beginTransaction();
session.update(model);
transaction.commit();
}catch(Exception e){
transaction.rollback();
e.printStackTrace();
}
return "news";
}
The problem is my code run smoothly, no errors but nothing was updated. My database is still the same when i tried to update. Is there anything wrong with my code ? Any help would be great
You have specified cascade in Supplier object, so it is applied if you save or update a Supplier. It means, you should either put the cascade in Category object, or change your logic somehow to save the supplier.
More explanation:
Modify the Category object as below:
#ManyToMany(mappedBy = "categories", cascade = CascadeType.ALL)
Set<Supplier> suppliers = new HashSet<Supplier>(0);
OR modify CategoryController.update() as below:
session.update(s);
try with flushing session before commit transaction and also close session after commit transaction:
#Action("/admin/category/update")
public String update() {
Supplier s = XHibernate.load(Supplier.class, "1");
if (!model.getSuppliers().contains(s)) {
model.getSuppliers().add(s);
s.getCategories().add(model);
}
}
Session session = XHibernate.openSession();
Transaction transaction = session.beginTransaction();
session.update(model);
session.flush();
transaction.commit();
session.close();
}
I solved my problems by change the following config to category class:
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "Suppliers_Categories", joinColumns = { #JoinColumn(name = "CategoryId") }, inverseJoinColumns = { #JoinColumn(name = "SupplierId") })
And also change in Supplier class:
#ManyToMany(mappedBy = "suppliers")
Now It worked.The problem is because I call the update from category so I need to specific the config in category class.
Related
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
Friends I have stuck in a Problem in Hibernate composite key.
Error : org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: IDP_UserManagerMapping, for columns: [org.hibernate.mapping.Column(idps)].I have Tried wit #transient and other things but not effective Please Help.
public class IDPUserManagerMapping implements Serializable {
private Long id;
private Users user;
private Users manager;
private Users assessor;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="user_id" ,referencedColumnName = "user_id"),
#JoinColumn(name="manager_id" ,referencedColumnName = "manager_id")
,#JoinColumn(name="assessor_id" ,referencedColumnName = "assessor_id")
})
#Cascade(CascadeType.ALL)
private Set<IDPMaster> idps = null;
#Id
#SequenceGenerator(sequenceName="idp_usermanagermapping_id_seq",name="idp_usermanagermapping_id_seq_gen")
#GeneratedValue(generator="idp_usermanagermapping_id_seq_gen")
#Column(name="id",nullable=false,unique=true,updatable=false)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<IDPMaster> getIdps() {
return idps;
}
public void setIdps(Set<IDPMaster> idps) {
this.idps = idps;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="user_id",nullable=false)
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="manager_id",nullable=false)
public Users getManager() {
return manager;
}
public void setManager(Users manager) {
this.manager = manager;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="assessor_id")
public Users getAssessor() {
return assessor;
}
public void setAssessor(Users assessor) {
this.assessor = assessor;
}
}
Second Entity
public class IDPMaster implements Serializable {
private Long id;
private Users managerUser;
private Users assessorUser;
private Users idpUsers;
#Id
#SequenceGenerator(name="idp_master_id_seq_gen",sequenceName="idp_master_id_seq")
#GeneratedValue(generator="idp_master_id_seq_gen")
#Column(name="id",nullable=false,unique=true)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="user_id",nullable=false)
public Users getIdpUsers() {
return idpUsers;
}
public void setIdpUsers(Users idpUsers) {
this.idpUsers = idpUsers;
}
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="manager_id")
public Users getManagerUser() {
return managerUser;
}
public void setManagerUser(Users managerUser) {
this.managerUser = managerUser;
}
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="assessor_id")
public Users getAssessorUser() {
return assessorUser;
}
public void setAssessorUser(Users assessorUser) {
this.assessorUser = assessorUser;
}
}
You should move the annotations
#OneToMany(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="user_id" ,referencedColumnName = "user_id"),
#JoinColumn(name="manager_id" ,referencedColumnName = "manager_id")
,#JoinColumn(name="assessor_id" ,referencedColumnName = "assessor_id")
})
#Cascade(CascadeType.ALL)
from private Set<IDPMaster> idps = null; to public Set<IDPMaster> getIdps() getter
Hibernate analyzes annotations by ID column and if you use annotations on getter there you should use getter annotations everywhere.
I have two beans classes - User and UserGroup:
User:
#Entity
#Table(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = -7482853955996650586L;
#Id
#Column(name = "id")
#GeneratedValue
private Integer id;
#Column(name = "login")
private String login;
#ManyToMany
#JoinTable(name = "user_x_userGroup",
joinColumns = {
#JoinColumn(
name = "user_id",
foreignKey = #ForeignKey(name = "FK_user_x_userGroup_user_id")
) },
inverseJoinColumns = {
#JoinColumn(
name = "userGroup_id",
foreignKey = #ForeignKey(name = "FK_user_x_userGroup_userGroup_id")
) })
private List<UserGroup> userGroups;
public User() {
this.userGroups = new ArrayList<>();
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLogin() {
return this.login;
}
public void setLogin(String login) {
this.login = login;
}
public List<UserGroup> getUserGroups() {
return this.userGroups;
}
public void setUserGroups(List<UserGroup> userGroups) {
this.userGroups = userGroups;
}
}
UserGroup:
#Entity
#Table(name = "userGroup")
public class UserGroup implements Serializable, Comparable<UserGroup> {
private static final long serialVersionUID = -5924845694417474352L;
#Id
#Column(name = "id")
#GeneratedValue
private Integer id;
#Column(name = "title")
private String title;
#ManyToMany(mappedBy = "userGroups")
private List<User> users;
public UserGroup() {
this.users = new ArrayList<>();
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public List<User> getUsers() {
return this.users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
I'm testing if list of user groups has size 1 in UserServiceTest:
public class UserServiceTest {
#BeforeClass
public static void prepareDatabase() throws DAOException {
MockDatabase.insertUsers();
MockDatabase.insertUserGroups();
}
#Test
public void shouldGetUserGroupSize() throws Exception {
UserService userService = new UserService();
User user = userService.find(1);
boolean condition = user.getUserGroups().size() == 1;
assertTrue("User groups size is incorrect!", condition);
}
}
Unfortunately it returns size 0. I can see in console that it inserts data to user and userGroup table.
Mocking data for database:
public static void insertUsers() throws DAOException {
UserService userService = new UserService();
User firstUser = new User();
firstUser.setLogin("first");
userService.save(firstUser);
User secondUser = new User();
secondUser.setLogin("second");
userService.save(secondUser);
}
public static void insertUserGroups() throws DAOException {
UserService userService = new UserService();
UserGroupService userGroupService = new UserGroupService();
UserGroup firstUserGroup = new UserGroup();
firstUserGroup.setTitle("Admin");
List<User> users = new ArrayList<>();
users.add(userService.find(1));
firstUserGroup.setUsers(users);
userGroupService.save(firstUserGroup);
UserGroup secondUserGroup = new UserGroup();
secondUserGroup.setTitle("Random");
userGroupService.save(secondUserGroup);
}
Here is find method:
public User find(Integer id) throws DAOException {
User result = (User) retrieveObject(User.class, id);
if (result == null) {
throw new DAOException("Object can not be found in database");
}
return result;
}
protected static Object retrieveObject(Class cls, Integer id) throws DAOException {
try {
Session session = Helper.getHibernateSession();
if (session == null) {
session = HibernateUtil.getSessionFactory().openSession();
Object o = session.get(cls, id);
session.close();
return o;
}
return session.get(cls, id);
} catch (HibernateException he) {
throw new DAOException(he);
}
}
And here save:
public User save(User user) throws DAOException {
storeObject(user);
return (User) retrieveObject(User.class, user.getId());
}
protected static void storeObject(Object object) throws DAOException {
try {
Session session = Helper.getHibernateSession();
session.saveOrUpdate(object);
session.flush();
session.beginTransaction().commit();
} catch (HibernateException he) {
rollback();
throw new DAOException(he);
}
}
It looks it saves data to user table and userGroup table but it doesn't save data to user_x_userGroup table. What can cause this problem?
You don't set the UserGroup instance you persist in the entity that is the owner of the relationship.
In UserGroup you declare :
#ManyToMany(mappedBy = "userGroups")
private List<User> users;
That means that the owner side of the bidirectional relation is the User entity.
In your actual code, the owner side (User) doesn't reference the UserGroup entity you want to persist, so the UserGroup entity will be persisted but it will not associated to the users field you have set in UserGroup.
So just set it suitably and it should be ok:
UserGroup firstUserGroup = new UserGroup();
firstUserGroup.setTitle("Admin");
List<User> users = new ArrayList<>();
// change
User user = userService.find(1);
List<UserGroup> groups = new ArrayList<>();
groups.add(firstUserGroup);
user.setUserGroups(groups);
// end change
users.add(user);
firstUserGroup.setUsers(users);
userGroupService.save(firstUserGroup);
...
You need to add casacade type to your relationship :
#ManyToMany(cascade = CascadeType.ALL)
Change below User.java and UserGroup.java classes relationships as below and try out it works.
User.java
#ManyToMany(targetEntity = User.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "user_x_userGroup",
joinColumns =
#JoinColumn(name = "user_id",foreignKey = #ForeignKey(name = "FK_user_x_userGroup_user_id")),inverseJoinColumns =
#JoinColumn( name = "userGroup_id",foreignKey = #ForeignKey(name = "FK_user_x_userGroup_userGroup_id")))
private List<UserGroup> userGroups;
UserGroup.java
#ManyToMany(targetEntity = UserGroup.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
joinColumns =
#JoinColumn(name ="userGroup_id",foreignKey = #ForeignKey(name = "FK_user_x_userGroup_userGroup_id")),inverseJoinColumns =
#JoinColumn( name = "user_id",foreignKey = #ForeignKey(name = "FK_user_x_userGroup_user_id")))
private List<User> users;
I have mapped a bidirectional relation between two classes. This is the situation: I have Trips and Users. Users should be able to subscribe for a trip and to unsubscribe. So I don't want to delete the obects, only the row in the join table. Currently, the subscription part works but not unsubscribing.
When I test it the changes are made in memory, but not in the database. What could be wrong? My code is shown below.
Trip:
#ManyToMany(mappedBy = "enrolledExecutionMoments", cascade = {
CascadeType.ALL, CascadeType.ALL }, fetch = FetchType.EAGER)
private List<User> tripExecutionMomentParticipators = new ArrayList<User>();
public TripExecutionMoment() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#JsonIgnore
public List<User> getTripExecutionMomentParticipators() {
return tripExecutionMomentParticipators;
}
public void setTripExecutionMomentParticipators(
List<User> tripExecutionMomentParticipators) {
this.tripExecutionMomentParticipators = tripExecutionMomentParticipators;
}
public void addEnrollement(User user) {
tripExecutionMomentParticipators.add(user);
}
public void removeEnrollement(User user) {
tripExecutionMomentParticipators.remove(user);
}
User:
#Entity
#Table(name = "t_user")
public class User {
#Id
#GeneratedValue
private int id;
#NotBlank(message = "err_invalid_mail")
#Email(message = "err_invalid_mail")
private String email;
#ManyToMany(cascade = { CascadeType.ALL, CascadeType.ALL }, fetch = FetchType.EAGER)
#JoinTable(joinColumns = { #JoinColumn(name = "userId") }, inverseJoinColumns = { #JoinColumn(name = "tripExecutionMomentId") })
private List<TripExecutionMoment> enrolledExecutionMoments = new ArrayList<TripExecutionMoment>();
public User() {
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getId() {
return id;
}
#JsonIgnore
public TripExecutionMoment getCheckedInTripExecutionMoment() {
return checkedInTripExecutionMoment;
}
public void setCheckedInTripExecutionMoment(
TripExecutionMoment checkedInTripExecutionMoment) {
this.checkedInTripExecutionMoment = checkedInTripExecutionMoment;
}
public void addEnrollement(TripExecutionMoment tripExecutionMoment) {
enrolledExecutionMoments.add(tripExecutionMoment);
}
public void removeEnrollement(TripExecutionMoment tripExecutionMoment) {
enrolledExecutionMoments.remove(tripExecutionMoment);
}
}
This is the code where I enroll(subscribe) and dis-enroll(unsubscribe)
#Transactional
public void enrollParticipator(User user,
TripExecutionMoment tripExecutionMoment)
throws AlreadyBoundException {
if (tripExecutionMoment.getTripExecutionMomentParticipators().contains(
user)) {
throw new AlreadyBoundException(
"This user is already enrolled for this trip");
}
tripExecutionMoment.addEnrollement(user);
user.addEnrollement(tripExecutionMoment);
getEntityManager().merge(tripExecutionMoment);
}
#Transactional
public void disenrollParticipator(User user,
TripExecutionMoment tripExecutionMoment) {
tripExecutionMoment.removeEnrollement(user);
user.removeEnrollement(tripExecutionMoment);
getEntityManager().merge(user);
}
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.