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

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

Related

Tomcat 8, Hibernate 5.2, MSSQL, HikariCP Criteria Search Returns Null

Currently we are doing a performance review and improvement of the DB CRUD function of the application and one of the reviews was using Hikari connection pool and a later version of Hibernate and Hibernate-HikariCP for our tests.
We are running this by upgrading the Hibernate version from 5.2.10 to 5.2.16, with Hikari at version 3.1.0. This entire exercise was tested with Tomcat 8 and MSSQL 2016.
For simplicity purposes, we tested on one simple search with one search condition using the entity User.
Hibernate XML Configuration:
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">org.hibernate.dialect.SQLServerDialect</property>
<property name="show_sql">true</property>
<!-- Hikari specific properties -->
<property name="hibernate.connection.provider_class">com.zaxxer.hikari.hibernate.HikariConnectionProvider</property>
<property name="hikari.maximumPoolSize">30</property>
<property name="hikari.idleTimeout">300000</property>
<property name="hikari.maxLifetime">1800000</property>
<!-- Database connection properties -->
<property name="hibernate.hikari.dataSourceClassName">com.microsoft.sqlserver.jdbc.SQLServerDataSource</property>
<property name="hibernate.connection.url">jdbc:sqlserver://192.168.0.102:1433;databaseName=migration1;user=sun;password=p#ssw0rd;</property>
<property name = "hibernate.connection.username">sun</property>
<property name = "hibernate.connection.password">p#ssw0rd</property>
<mapping class="com.hib.model.Outlet"/>
<mapping class="com.hib.model.Receipt"/>
<mapping class="com.hib.model.Cashier"/>
<mapping class="com.hib.model.Category"/>
<mapping class="com.hib.model.Item"/>
<mapping class="com.hib.model.PayMethodName"/>
<mapping class="com.hib.model.Pos"/>
<mapping class="com.hib.model.Purchase"/>
<mapping class="com.hib.model.User"/>
</session-factory>
</hibernate-configuration>
The HibernateUtil class is quite simple, currently as:
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from standard (hibernate.cfg.xml)
// config file.
Configuration configuration = new Configuration().configure();
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties());
sessionFactory = new Configuration().configure().buildSessionFactory(builder.build());
} catch (Throwable th) {
System.err.println("Initial SessionFactory creation failed " + th);
throw new ExceptionInInitializerError(th);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}
The User Entity, annotated, excluding the getter / setters:
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.XmlRootElement;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.UpdateTimestamp;
#Entity
#Table(name = "User")
#XmlRootElement
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "id")
#NotNull
#GeneratedValue(generator="`id`")
#GenericGenerator(name="`id`", strategy = "increment")
private Integer id;
#NotNull
#Column(name = "FullName")
private String fullName;
#Column(name = "NickName")
private String nickName;
#Column(name = "Tel")
private String tel;
#Column(name = "Rank")
private String rank;
#NotNull
#Column(name = "Email")
private String email;
#NotNull
#Column(name = "Password")
private String password;
#Lob
#Column(name = "PhotoUpload")
private String photoUpload;
#Version
#Column(name = "CreateDate")
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
private Date createDate;
#Version
#Column(name = "EditDate")
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
private Date editDate;
#Column(name = "CreateBy")
private String createBy;
#Column(name = "ActiveInd")
private String activeInd;
#Column(name = "LastLogin")
#Temporal(TemporalType.TIMESTAMP)
private Date lastLogin;
#Column(name = "tenant_id")
private String tenantId;
#NotNull
#Column(name = "group_id")
private String groupId;
public User() {
}
public User(Integer id, String fullName,String nickName,String tel,String rank,String email,String password,String photoUpload,Date createDate,Date editDate,String createBy,String activeInd,Date lastLogin, String tenantId, String groupId) {
this.id = id;
this.fullName = fullName;
this.nickName = nickName;
this.tel =tel;
this.rank=rank;
this.email =email;
this.password = password;
this.photoUpload = photoUpload;
this.createDate =createDate;
this.editDate =editDate;
this.createBy =createBy;
this.activeInd = activeInd;
this.lastLogin=lastLogin;
this.tenantId= tenantId;
this.groupId =groupId;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof User)) {
return false;
}
User other = (User) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.example.dao.User[ id=" + id + " ]";
}
The function of searching the particular Users is in this method of the User Service class:
#Override
public List<User> getUserById(Integer id) {
List<User> result = null;
Session session = getSession();
try {
Criteria criteria = getSession().createCriteria(User.class);
Criteria criteria = getSession().(User.class);
criteria.add(Restrictions.eq("id", id));
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
result = query.getResultList();
}catch (HibernateException e){
userLogger.error("Error found: " + e);
}finally {
if (session != null)
session.close();
}
return result;
}
When tested, if the original C3P0 connection pool is used, the search function above will return the expected result of the matching record corresponding to the Id number passed in (numeric value).
However, when switched to HikariCP, the result returns null. However, if we write a NativeQuery instead of Criteria, it does not get affected.
Subsequently, we tried three alternatives to see if it works but with different errors:
Option 1: - returns an IllegalArgumentException, entity User not mapped
TypedQuery<User> query = session.createQuery("select u from "+User.class.getSimpleName()+" u",User.class) ;
result = query.getResultList();
Option 2: - returns an IllegalArgumentException, not an entity User.
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.select(root);
Query<User> q = session.createQuery(cq);
result = q.getResultList();
Option 3 - returns a mapping exception, unknown entity User:
String sql = "SELECT * FROM USER_LOG WHERE id = :number_id";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(User.class);
query.setParameter("number_id", id);
result = query.list();
As HikariCP has done a lot of improvements for the other Native Queries, we felt it is better if we can still keep on using that CP in favor of the C3P0 pool. However, we are still stuck as in unsure to which approach in the Service class method is best used.
We appreciate any advise on help on this. Thanks.

EntityManager merge is not working while updating OneToMany Side

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
}

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.

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

Getting " org.apache.openjpa.persistence.PersistenceException: null keys not allowed" error when persist objects.

I am using OpenJPA with Eclipse to persist object. I created a simple one to one unidirectional application. But it is giving Foreign key null error.
Student Entity
#Entity
public class Student implements Serializable {
#Id
private int id;
private String name;
private static final long serialVersionUID = 1L;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "dept_id", unique = true, nullable = true, insertable = true, updatable = true, referencedColumnName = "id")
private Department department;
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 Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public String toString() {
return "\n\nID:" + id + "\nName:" + name + "\n\n" + department;
}
}
Department Entity
#Entity
#Table(name = "department")
public class Department implements Serializable {
#Id
private int id;
private String name;
private static final long serialVersionUID = 1L;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String deptName) {
this.name = deptName;
}
public String toString() {
return "Department id: " + getId() + ", name: " + getName();
}
}
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="IBMJPADemo" transaction-type="RESOURCE_LOCAL">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>com.ibm.jpa.onetoone.model.Department</class>
<class>com.ibm.jpa.onetoone.model.Student</class>
<properties>
<property name="openjpa.ConnectionURL" value="jdbc:mysql://localhost:3306/test" />
<property name="openjpa.ConnectionDriverName" value="com.mysql.jdbc.Driver" />
<property name="openjpa.ConnectionUserName" value="root" />
<property name="openjpa.ConnectionPassword" value="root" />
<property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO" />
<property name="openjpa.RuntimeUnenhancedClasses" value="supported"/>
<property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO"/>
<!-- property name="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)" /-->
<!-- property name="openjpa.jdbc.MappingDefaults"
value="ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict" /-->
</properties>
</persistence-unit>
</persistence>
Client Program
public class OneToOneClient {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence
.createEntityManagerFactory("IBMJPADemo");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Student student = new Student();
student.setId(2537);
student.setName("K.Senthuran");
Department dept = new Department();
dept.setId(100);
dept.setName("IT");
student.setDepartment(dept);
em.persist(student);
em.flush();
tx.commit();
em.close();
}
}
Error
Exception in thread "main" org.apache.openjpa.persistence.PersistenceException: null keys not allowed
at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1817)
at org.apache.openjpa.kernel.DelegatingBroker.flush(DelegatingBroker.java:1037)
at org.apache.openjpa.persistence.EntityManagerImpl.flush(EntityManagerImpl.java:652)
at com.ibm.jpa.onetoone.client.OneToOneClient.main(OneToOneClient.java:32)
Caused by: java.lang.NullPointerException: null keys not allowed
at org.apache.commons.collections.map.AbstractReferenceMap.put(AbstractReferenceMap.java:248)
at org.apache.openjpa.kernel.ManagedCache.assignObjectId(ManagedCache.java:189)
at org.apache.openjpa.kernel.BrokerImpl.assignObjectId(BrokerImpl.java:4949)
at org.apache.openjpa.kernel.BrokerImpl.setStateManager(BrokerImpl.java:4046)
at org.apache.openjpa.kernel.StateManagerImpl.assertObjectIdAssigned(StateManagerImpl.java:636)
at org.apache.openjpa.kernel.StateManagerImpl.afterFlush(StateManagerImpl.java:1084)
at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2162)
at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2037)
at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1808)
... 3 more
Please help to solve this issue.
Thanks & Regards,
K.Senthuran
Since you are using Uni-Directional Mapping, so persisting your Student will not persist your Department too. So, you need to make sure that, while persisting the Student, the Department entity reference used is already persisted in the database, else you will get exception.
So, just persist the department, before you persist the student. I think that will solve your issue.
If you want that, persisting your student also persist the department, then you would need to use bi-directional mapping. i.e. Use a reference of Student in Department, with #OneToOne mapping, specifying a mappedBy attribute.
I found the solution for this issue.
First of all we have to mention the primary key attribute with #Id and #Column annotation.
Then we have to add the following line in persistence.xml.
<property name="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)" />

Categories

Resources