EntityManager merge is not working while updating OneToMany Side - java

I have two entities with bidirectional OneToMany relationship . eg
Employee.java
package com.jpademo.entities;
import java.io.Serializable;
import javax.persistence.*;
/**
* Entity implementation class for Entity: Employee
*
*/
#Entity
#Table(name = "Employee")
#SequenceGenerator(name = "Emp_Id_Gen", sequenceName = "EMP_ID_SEQ_GEN", allocationSize = 1)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "Emp_Id_Gen")
private Integer employeeId;
private String Name;
#ManyToOne(cascade = { CascadeType.ALL })
private Department department;
public Integer getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
Department.java
package com.jpademo.entities;
import java.io.Serializable;
import java.util.List;
import javax.persistence.*;
/**
* Entity implementation class for Entity: Department
*
*/
#Entity
#Table(name = "Department")
#SequenceGenerator(name = "Dept_Id_Gen", sequenceName = "DEPT_ID_SEQ_GEN", allocationSize = 1)
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "Dept_Id_Gen")
private Integer deptId;
private String deptName;
#OneToMany(mappedBy = "department", cascade = { CascadeType.MERGE,
CascadeType.PERSIST })
private List<Employee> employees;
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
EmployeeDAO.java
package com.jpademo.dao;
import java.util.Arrays;
import java.util.List;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import com.jpademo.entities.Department;
import com.jpademo.entities.Employee;
#Stateless(name = "employeeDAO")
#LocalBean
public class EmployeeDAO {
#PersistenceContext(unitName = "JPA2Pratice")
private EntityManager em;
public void saveEmployee() {
Employee emp = new Employee();
emp.setName("Mukesh Saini");
Department dept = new Department();
dept.setDeptName("Physics");
emp.setDepartment(dept);
em.persist(emp);
}
public void addEmployeeToDepartment() {
TypedQuery<Department> departmentQuery = em
.createQuery(
"SELECT dept FROM Department dept WHERE dept.deptId = :departmentId",
Department.class);
departmentQuery.setParameter("departmentId", 1);
Department dept = departmentQuery.getSingleResult();
// Employee already in department
List<Employee> currentEmpinDept = dept.getEmployees();
// Employee that need to be add in department
TypedQuery<Employee> employeeQuery = em.createQuery(
"SELECT emp FROM Employee emp WHERE emp.employeeId IN :empIds",
Employee.class);
employeeQuery.setParameter("empIds", Arrays.asList(2, 3));
List<Employee> employees = employeeQuery.getResultList();
currentEmpinDept.addAll(employees);
dept.setEmployees(currentEmpinDept);
em.merge(dept); // this does not update employee department
}
}
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="JPA2Pratice">
<jta-data-source>jdbc/MySQLDataSource</jta-data-source>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables" />
<property name="eclipselink.logging.level.sql" value="FINE" />
<property name="eclipselink.logging.parameters" value="true" />
</properties>
</persistence-unit>
</persistence>
In method addEmployeeToDepartment in EmployeeDAO em.merge(dept) is not updating the Employee Department, what am I doing wrong here. Or Does cascade works from Parent to child only ?
Thanks.

I don't think you need to worry about a merge here, you just need to update both sides of the relationship. The employee objects returned from your query are already managed/attached.
But according to here and here it's not enough to simply set the new list of Employee objects on the parent Department object. You have to update the reference to the Department on each child Employee as well.
So you could just add:
// ...
List<Employee> employees = employeeQuery.getResultList();
// NEW: Update owning side
for (Employee e : employees) {
e.setDepartment(dept);
}
currentEmpinDept.addAll(employees);
dept.setEmployees(currentEmpinDept);
//em.merge(dept); - DON'T THINK THIS IS NEEDED
}

Related

Hibernate - Foreign key is empty while using mappedBy

I've 2 tables Employee & Vehicle, where one employee can have multiple vehicles.
Below is the mapping that I've defined:
Employee.java
#Entity(name = "emp_details")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int empId;
#OneToMany(cascade = CascadeType.ALL, mappedBy="employee")
private List<Vehicle> vehicles = new ArrayList<>();
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public List<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(List<Vehicle> vehicles) {
this.vehicles = vehicles;
}
}
Vehicle.java
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int vehicleId;
#ManyToOne
#JoinColumn(name="empId")
private Employee employee;
private String name;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public int getVehicleId() {
return vehicleId;
}
public void setVehicleId(int vehicleId) {
this.vehicleId = vehicleId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Main class
public class HibernateTest {
public static void main(String[] args) {
Employee emp = new Employee();
Vehicle vehicle = new Vehicle();
vehicle.setName("Honda");
emp.getVehicles().add(vehicle);
SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
Session session = sFactory.openSession();
session.beginTransaction();
session.save(emp);
session.getTransaction().commit();
session.close();
StandardServiceRegistryBuilder.destroy(sFactory.getSessionFactoryOptions().getServiceRegistry());
}
}
But when I execute this, Vehicle.employee_id is empty. I was expecting that my foreign key will be inserted there.
What am I missing?
Thank You
You need to show us the code that persists your entities, but my guess is that you are not setting the employee to the vehicle.
You need to manage both sides of bidirectional entity relationship. Your code should look something like this
employee.getVehicles().add(vehicle);
vehicle.setEmployee(employee);
session.save(employee);
UPDATE:
In this case, Vehicle is the owning side of the relation because the foreign key is in its database table. You just added the new vehicle to the employee's list of vehicles. When you save the employee, there's nothing to change in Employees database, and the save operation cascades to Vehicle. Vehicle does not have its employee set, it is null, so it puts null in empId column
Bottom line, you have to make sure both sides of bidirectional relationship are wired up correctly.
If you have a bidirectional relationship you have to
set the relation on both sides.
That means you have to set the employee for your vehicle also.
You can do this by calling
vehicle.setEmployee(emp);
and then store or update your entity (if it's not attached to session already).
Usually to set a bidirectional relationship you provide special methods in your entities.
public class Vehicle {
...
public void setEmployee(Employee employee) {
this.employee = employee;
employee.addVehicle(this)
}
...
}
public class Employee {
...
public void addVehicle(Vehicle v) {
if(!vehicles.contains(v)) {
vehicles.add(v);
}
if(!this.equals(v.getEmployee()) {
v.setEmployee(this);
}
}
...
}
In your Employee entity class, you haven't used #Column annotation on empId. In Vehicle class you are referencing Employee using #JoinColumn(name="employee_id"),so column employee_id must exist in emp_details table. So you need to modify your Employee class to something with
#Id
#GeneratedValue
#Column(name="employee_id")
private int empId;
I think need to edit your code. you miss the entity jpa rule. you can some search jpa entity. you read description for you. link : enter link description here
(Modified your Employee.java)
#Entity(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int idx;
#OneToMany(cascade = {CascadeType.ALL}, mappedBy = "employee")
private List<Vehicle> vehicle = new ArrayList<>();
...
}
(Modified your Vehicle.java)
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int idx;
#ManyToOne
#JoinColumn(name="employee_id")
private Employee employee;
...
}
You only need to cascade onetoMany from Employee Entity

JPA: Mapping ManyToMany relationship with additional property [duplicate]

This question already has answers here:
JPA many to many with extra column
(3 answers)
Closed 7 years ago.
I have many-to-many relationship between Employee and SkillSet table, with additional column numberOfYears for each relation
employeeId skillSetId numberOfYears
10 101 2
I am new to JPA and unable to define the entities with relationship. Should I define a new entity class for Employee_SkillSet table? Or can I have many to many relationship defined in both Employee and SkillSet class? Where do I specify numberOfYears column?
Edit: Seems duplicate, but I had explicit requirement of using #IdClass, and one of the entities was #MappedSuperclass, so have to define both ID instance, and referred entity object.
Since you need an additional field for the tuple (Employee, SkillSet), you have to make another entity.
#Entity
public class Employee implements Serializable {
private #Id Long id;
#OneToMany(mappedBy="employee")
private List<EmployeeSkillSet> skillSets;
}
#Entity
public class SkillSet implements Serializable {
private #Id Long id;
}
#Entity
public class EmployeeSkillSet implements Serializable {
private #Id Long id;
private #ManyToOne Employee employee;
private #ManyToOne SkillSet skillSet;
private #Basic int numberOfYears;
}
Of course you can choose to use a #IdClass to make ("employee", "skillSet") the primary key of EmployeeSkillSet like so:
#Entity #IdClass(EmployeeSkillSet.Key.class)
public class EmployeeSkillSet implements Serializable {
private #Id #ManyToOne Employee employee;
private #Id #ManyToOne SkillSet skillSet;
private #Basic int numberOfYears;
public static class Key implements Serializable {
private Long employee; // plus getter+setter
private Long skillSet; // plus getter+setter
// plus hashCode, equals
}
}
Although you can use #ManyToMany annotation is better for performance reasons define two many to one relations in order to model the Many To Many relationship.
You will need 4 artifacts, there are
Employee Entity
SkillSet Entity
EmployeeSkillSet Relation entity (here you can specify numberOfYears Column)
EmployeeSkillSetPK (Primary Key for EmployeeSkillSet Relation entity)
The code would be something like this
Employee
package <your_package>;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
*
*/
#Entity
#Table(name="EMPLOYEE")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="EMPLOYEEID")
private int id;
// Rest of columns and getter and setters for all
}
SkillSet
package <your_package>;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
*
*/
#Entity
#Table(name="SKILLSET")
public class SkillSet implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="SKILLSETID")
private int id;
// Rest of columns and getter and setters for all
}
EmployeeSkillSetPK
/**
*
*/
package <your_package>;
import java.io.Serializable;
import javax.persistence.Embeddable;
/**
*
*/
#Embeddable
public class EmployeeSkillSetPK implements Serializable {
#ManyToOne
private Employee emp;
#ManyToOne
private SkillSet sk;
/**
* #return the employee
*/
public Employee getEmployee() {
return emp;
}
/**
* #param employee the employee to set
*/
public void setEmployee(Employee employee) {
this.employee = employee;
}
/**
* #return the sk
*/
public SkillSet getSkillSet() {
return sk;
}
/**
* #param sk the sk to set
*/
public void setSkillSet(SkillSet sk) {
this.sk = sk;
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
EmployeeSkillSetPK that = (EmployeeSkillSetPK) o;
if (employee != null ? !employee.equals(that.employee) : that.employee != null) {
return false;
}
if (sk != null ? !sk.equals(that.sk) : that.sk != null) {
return false;
}
return true;
}
public int hashCode() {
int result;
result = (employee != null ? employee.hashCode() : 0);
result = 31 * result + (sk != null ? sk.hashCode() : 0);
return result;
}
}
EmployeeSkillSet
/**
*
*/
package <your_package>;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
*
*/
#Entity
#Table(name="EMPLOYEESKILLSET")
#AssociationOverrides({ #AssociationOverride(name = "pk.employee", joinColumns = #JoinColumn(name = "EMPLOYEEID")),
#AssociationOverride(name = "pk.sk", joinColumns = #JoinColumn(name = "SKILLSETID")) })
public class EmployeeSkillSet implements Serializable {
#EmbeddedId
private EmployeeSkillSetPK pk = new EmployeeSkillSetPK();
#Column
private Integer numberOfYears;
#Transient
public Employee getEmployee() {
return pk.getEmployee();
}
public void setEmployee(Employee employee) {
pk.setEmployee(employee);
}
#Transient
public SkillSet getSkillSet() {
return pk.getSkillSet();
}
public void setSkillSet(SkillSet sk) {
pk.setSkillSet(sk);
}
public Integer getNumbersOfYears() {
return numberOfYears;
}
public void setNumbersOfYears(Integer numberOfYears) {
this.numberOfYears = numberOfYears;
}
}
This is for JPA 1.0, I cannot test the code right now, but it should work.
Note that I wrote the table and columns names on my own. Adapt it as you wish.

Spring MVC - Setting values by default in Controllers

Based on the following mapping (at the bottom of question) I need to know how to set an specific value in the "department_id" in the Employee class.
Employee
--------------------------------------------
id | firstname | lastname | department_id
--------------------------------------------
1 | David | Smith | 1
Department
-----------
id | name
-----------
1 | Dep A
2 | Dep B
3 | Dep C
saveEmployee method (EmployeeController class):
#RequestMapping(value = "/saveEmployee", method = RequestMethod.POST)
public String saveEmployee(#ModelAttribute("employee") Employee employee){
/* I need to set the department "id" (foreign key) into the Employee
table directly in this method. */
int id = 1; // 2 or 3...
/* The "department_id" in the Employee class should
to receive the "id" value. */
employee.setDepartment(id); // It does not work.
employeeService.saveEmployee(employee);
return "redirect:/employees";
}
Employee class:
#Entity
public class Employee{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String firstname;
private String lastname;
#ManyToOne
#JoinColumn(name = "department_id")
private Department department;
// Getters and Setters
}
Department class:
#Entity
public class Department{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
// Getters and Setters
}
Look carefully at your Employee class:
#Entity
public class Employee{
...
#ManyToOne
#JoinColumn(name = "department_id")
private Department department;
/* THIS IS NOT AN INTEGER DATA TYPE, IT'S A DEPARTMENT DATA TYPE.
SO THE SETTER FOR THIS WILL LOOK SOMEWHAT LIKE THIS:*/
//Setter
public void setDepartment(Department department) {
this.department = department
}
...
// Getters and Setters
}
In order to set a department create an instance your Department and then send it through setter:
#RequestMapping(value = "/saveEmployee", method = RequestMethod.POST)
public String saveEmployee(#ModelAttribute("employee") Employee employee){
int id = 1; // 2 or 3...
Department temporaryDepartment = new Department();
temporaryDepartment.setId(id);
employee.setDepartment(temporaryDepartment);
employeeService.saveEmployee(employee);
return "redirect:/employees";
}
Actually your setDepartment receives a Department instance. So you have to do this:
int id = 1;
Department department = new Department(); //Or you can use Autowired
department.setId(id); // Set Id Department
employee.setDepartment(department); // A Department instance
employeeService.saveEmployee(employee);
return "redirect:/employees";
Actually, You have set Generation strategy as shown below
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
on Department class which means you would want hibernate to generate the ids for you so that you don't have to worry about setting the department id. You just need to set the department name.
Below is the working code, JPAEmployeeTest.java:
package com.chatar.hibernate.receipes.example;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import com.chatar.hibernate.receipes.example.domain.annotations.Department;
import com.chatar.hibernate.receipes.example.domain.annotations.Employee;
public class JPAEmployeeTest {
public static void main(String[] args) {
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "employee" );
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
Employee employee = new Employee();
Department department = new Department();
department.setName("Engineering");
employee.setFirstName("Johny");
employee.setLastName("Walker");
employee.setDepartment(department);
entityManager.persist(employee);
entityManager.getTransaction().commit();
entityManager.close();
}
}
And my domain object, Employee.java
package com.chatar.hibernate.receipes.example.domain.annotations;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
#Entity
public class Employee implements Serializable {
private static final long serialVersionUID = -5641563180459243167L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "department_id" )
private Department department;
public long getId() {
return 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 Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
And Department.java
package com.chatar.hibernate.receipes.example.domain.annotations;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Department implements Serializable {
private static final long serialVersionUID = -598469568850009702L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
My persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="employee" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.chatar.hibernate.receipes.example.domain.annotations.Employee</class>
<class>com.chatar.hibernate.receipes.example.domain.annotations.Department</class>
<properties>
<property name="javax.persistence.jdbc.driver"
value = "org.apache.derby.jdbc.EmbeddedDriver"/>
<property name="javax.persistence.jdbc.url" value = "jdbc:derby://localhost:1527/BookShopDB" />
<property name="javax.persistence.jdbc.user" value="book" />
<property name="javax.persistence.jdbc.password" value = "book" />
<property name="hibernate.dialect" value = "org.hibernate.dialect.DerbyDialect" />
</properties>
</persistence-unit>
</persistence>
My Database output:
ij> select * from employee;
ID |FIRST_NAME |LAST_NAME |DEPARTMENT&
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
163840 |Johny |Walker |1
1 row selected
ij> select * from department;
ID |NAME
----------------------------------------------------------------------------------------------------------------
1 |Engineering
1 row selected
ij>
Note - I'm using GenerationType.TABLE as somehow AUTO was not working for my database i.e. Debry. Also, I have set CascadeType.ALL so that when I save Employee entity, Hibernate saves all references entities e.g. Department in my case.

java.lang.NoSuchMethodError: javax.persistence.JoinColumn.foreignKey

Please help me with this error. Follow my code below:
This my class/entity Person:
package entities;
import java.util.Calendar;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
#Entity
public class Person {
#Id
#GeneratedValue
private long id;
#Column
private String name;
#Temporal(TemporalType.DATE)
private Calendar dateBirth;
#Column
private String email;
// MappedBy: Name of the instance that will indicate to whom One belongs, that is, an experience.
// It should say which person is associated
// TargetEntity: Informs the entity we are associating
// FetchType.Lazy: Was chosen for performance
// Cascade: ALL to allow changes in all relationships.
#OneToMany(mappedBy = "person", targetEntity = Experience.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Experience> experiences;
// Getters and setters
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Calendar getDateBirth() {
return dateBirth;
}
public void setDateBirth(Calendar dateBirth) {
this.dateBirth = dateBirth;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Experience> getExperiences() {
return experiences;
}
public void setExperiences(List<Experience> experiences) {
this.experiences = experiences;
}
}
This is my class/entity Experience:
package entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
#Entity
public class Experience {
#Id
#GeneratedValue
private long id;
#ManyToOne
#JoinColumn(name="idPerson", referencedColumnName="id")
private Person person;
#Column
private String company;
#Column
private String activities;
// Getters and setters
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getActivities() {
return activities;
}
public void setActivities(String activities) {
this.activities = activities;
}
}
My persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="professionalmanagementDB">
<!-- JPA implementation -->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- Entities -->
<class>entities.Person</class>
<class>entities.Experience</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/professionalmanagementDB" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="" />
</properties>
</persistence-unit>
</persistence>
So, I'm trying to use this code to make a query:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("professionalmanagementDB");
EntityManager manager = factory.createEntityManager();
String jpql = "select p ";
jpql += "from Person";
Query query = manager.createQuery(jpql);
List<Person> list = query.getResultList();
But always happen this error:
java.lang.NoSuchMethodError: javax.persistence.JoinColumn.foreignKey()Ljavax/persistence/ForeignKey;
org.hibernate.cfg.AnnotationBinder.bindManyToOne(AnnotationBinder.java:2881)
org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1795)
org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:963)
org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:796)
org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3790)
org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3744)
org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1410)
org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:843)
org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:397)
org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:842)
org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:73)
org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:54)
javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:63)
javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:47)
dao.PersonDAO.readAll(PersonDAO.java:65)
manager.PersonManager.getPersons(PersonManager.java:17)
org.apache.jsp.HomeScreen_jsp._jspService(HomeScreen_jsp.java:87)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
My maven dependencies is as follows:
javaee-api:7.0
hibernate-jpa-2.0-api:1.0.1.Final
hibernate-entitymanager:4.3.4.Final
mysql-connector-java:5.1.29
I don't know why Hibernate is wanting that foreignKey attribute on the #JoinColumn annotation, but it was added in JPA 2.1. Use the JPA 2.1 API dependency instead of the 2.0.
The many to one mapping with #JoinColumn(name="idPerson", referencedColumnName="id") means idPerson in the Experience class is related to the id of the Person table.
#ManyToOne
#JoinColumn(name="idPerson", referencedColumnName="id")
private Person person;
Here there is no idPerson I believe that's the problem. You must specify a foreign key for the mapping, between Person and Experiance like this
Person
id col col1
-----------
1 s s
Experience
id personId col col1
---------------------
1 1 s d
2 1 f d
Now you can specify multiple Experience for a person by
#ManyToOne // more than one experiance for a person
#JoinColumn // for condition
(name="personId", referencedColumnName="id") // for condition Experience.personId==person.id

JPA/Eclipselink how to handle circular/cyclic relationships

I have a question regarding circular relationships in JPA, and here in particular with Eclipselink JPA implementation. Sorry if the question is a bit long, but I try to be as precise as possible.
Let's take the simple example of Department and Employee where a Department has a one-to-many "employees" relationship (and hence the reverse many-to-one "department" relationship from Employee to Department). Now let's add a one-to-one relationship "manager" from Department towards Employee (one of the Employees of the Department is the manager of that same Department). That introduces a circular relationship between the two entities and both tables will have a foreign key referencing the other table.
I would like to be able to do all the inserts without getting a Foreign key constraint violation. So, my idea was to first insert all employees (without setting the department relationship), then insert the Department (with its manager being set), and eventually update all the employees to set their Department.
I know that I could use flush() to force the order of insert execution but I was told that it should be avoided and hence wondering if there is a way to tell JPA/Eclipselink that Department should be inserted first, then Employee.
In Eclipselink, I did try to add Employee as a constraint dependency of the classdescriptor of the Department class but it still gives error randomly.
Here is a code example illustrating this (the issue occurs randomly):
Department class:
package my.jpa.test;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Persistence;
/**
* Entity implementation class for Entity: Department
*
*/
#Entity
public class Department implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(fetch = FetchType.EAGER)
private List<Employee> employees;
#OneToOne
#JoinColumn(name = "manager", nullable = false)
private Employee manager;
private static final long serialVersionUID = 1L;
public Department() {
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test-jpa");
EntityManager em = emf.createEntityManager();
Department d = new Department();
Employee manager = new Employee();
manager.setLastName("Doe");
d.setManager(manager);
Employee e1 = new Employee();
e1.setLastName("Doe");
Employee e2 = new Employee();
e2.setLastName("Smith");
em.getTransaction().begin();
em.persist(d);
manager.setDepartment(d);
e1.setDepartment(d);
e2.setDepartment(d);
em.persist(e1);
em.persist(e2);
em.persist(manager);
em.persist(d);
manager.setDepartment(d);
e1.setDepartment(d);
e2.setDepartment(d);
em.merge(manager);
em.merge(e1);
em.merge(e2);
em.getTransaction().commit();
em.clear();
Department fetchedDepartment = em.find(Department.class, d.getId());
System.err.println(fetchedDepartment.getManager().getLastName());
System.err.println(new ArrayList<Employee>(fetchedDepartment.getEmployees()));
}
public Employee getManager() {
return manager;
}
public void setManager(Employee manager) {
this.manager = manager;
}
}
Employee class:
package my.jpa.test;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
/**
* Entity implementation class for Entity: Employee
*
*/
#Entity
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String lastName;
#ManyToOne
private Department department;
#OneToOne(mappedBy = "manager")
private Department managedDepartment;
public Employee() {
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public Department getManagedDepartment() {
return managedDepartment;
}
public void setManagedDepartment(Department managedDepartment) {
this.managedDepartment = managedDepartment;
}
#Override
public String toString() {
return "Employee " + getLastName();
}
}
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="test-jpa">
<class>my.jpa.test.Department</class>
<class>my.jpa.test.Employee</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
<property name="eclipselink.ddl-generation.output-mode" value="database" />
<property name="eclipselink.logging.level" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
</properties>
</persistence-unit>
</persistence>
Maven dependencies:
<dependencies>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.172</version>
</dependency>
</dependencies>
IMHO with this model you don't really have the choice.
Insert department (without manager)
insert employee (with departments)
flush
update department manager.
Deleting will probably be a mess too
Otherwise you could create an association table between department and employee to hold an isManager attribute.
Or put this last in employee table (not very normalized but well...)
From a general point of view it seems that circular reference are not advised in a relational model :
In SQL, is it OK for two tables to refer to each other?
I think that if you configure the departament column in Employee to allow null and set cascades correctly it can solve the problem. And please, do not use flush

Categories

Resources