JPA with composite key and secondary table - java

I'm new to JPA an I have a problem when trying to work with a secondary table and a composite key.
I get the following error message when I try to add, delete or update:
Provided id of the wrong type Expected: class EmployeePK, got class
java.lang.Integer
#Entity
#IdClass(EmployeePK.class)
#Table(name="specialemployee")
#SecondaryTable(name = "employeeTypeAndSalary", pkJoinColumns = {
#PrimaryKeyJoinColumn(name = "employee_Id"),
#PrimaryKeyJoinColumn(name = "employee_Email") })
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
public enum EmployeeType {
WORKER, FOREMAN, MANAGEMENT
}
#Id
private int id;
#Column(name = "EMP")
#Embedded
private Name name;
#Id
private String email;
private Date birthDate;
#Lob
private String comments;
#Column(name = "EMP_SALARY", table = "employeeTypeAndSalary")
private double salary;
#Column(name = "EMP_TYPE", table = "employeeTypeAndSalary")
#Enumerated(EnumType.STRING)
private EmployeeType employeeType;
public Employee() {
super();
}
public Employee(int id, Name name, String email, double salary, String birthDate,
String comments, EmployeeType employeeType) {
super();
this.id = id;
this.name = name;
this.email = email;
this.salary = salary;
try {
this.birthDate = java.sql.Date.valueOf(birthDate);
} catch (Exception e) {
logging.error("error on creating date" + " :" + e);
this.birthDate = java.sql.Date.valueOf("1900-00-00");
}
this.comments = comments;
this.employeeType = employeeType;
}
//getters and setters
}
public class EmployeePK implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String email;
// non-arg default constructor
public EmployeePK() {
super();
}
public EmployeePK(int id, String email){
this.id = id;
this.email = email;
}
public int getId() {
return id;
}
protected void setId(int id) {
this.id = id;
}
public String getEmail() {
return email;
}
protected void setEmail(String email) {
this.email = email;
}
public boolean equals(Object o) {
return ((o instanceof EmployeePK) &&
email.equals(((EmployeePK)o).getEmail()) &&
id == ((EmployeePK) o).getId());
}
public int hashCode() {
return (int) (email.hashCode() + id);
}
}
#Embeddable
public class Name implements Serializable {
private static final long serialVersionUID = 1L;
private String firstName;
private String lastName;
public Name() {
super();
}
#Override
public String toString() {
return "Name [firstName=" + firstName + ", lastName=" + lastName + "]";
}
public Name(String firstName, String lastName) {
super();
this.firstName = firstName;
this.lastName = lastName;
}
// getters and setters
}
I have been looking at it for some time now and I don't see what I'm doing wrong. Any advice would be welcome.
Thanks.
Edited :
Name name1 = new Name("Johnn", "Doe");
Employee employee1 = new Employee(1, name1, "employee1#hotmail.com",
1857.87, "1976-05-12", "ready for promotion",
EmployeeType.MANAGEMENT);
addEmployee(employee1);
private static void addEmployee(Employee employee) {
EntityManagerFactory emf = Persistence
.createEntityManagerFactory("JPA_excercise");
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
em.persist(employee);
em.getTransaction().commit();
} catch (Exception e) {
logging.error("This error has occured when adding a employee"
+ " :" + e);
} finally {
em.close();
emf.close();
}
}

Problem found. Add method didn't had any problems. Issue in update method where I forgot to change the logging text so it seemed that the problem whas in teh add method. Issue solved

Related

H2 ERROR: Referential integrity constraint violation - Hibernate one to one mapping

I have two entity (Instructor, InstructorDetail) which have one to one relation.
instructor_detail_id of Instructor entity has a foreign key to the id column of InstructorDetail. So, according to my requirement, when an Instructor is deleted, corresponding instructorDetail also needs to be deleted, but not the vice versa. Now, when I am trying to delete an instructorDetail, it is throwing the referencial integrity constraint error.
Note: I am using H2 db.
Following are the code snippets.
Instructor -
import javax.persistence.*;
#Table(name="instructor")
#Entity
public class Instructor implements IdentityMarker<Integer>{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="instructor_id")
private int id;
#Column(name="name")
private String name;
#Column(name="email")
private String email;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "instructor_detail_id")//, referencedColumnName = "id")
private InstructorDetail instructorDetail;
public Instructor(){
}
public Instructor(String name, String email) {
this.name = name;
this.email = email;
}
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 String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public InstructorDetail getInstructorDetail() {
return instructorDetail;
}
public void setInstructorDetail(InstructorDetail instructorDetail) {
this.instructorDetail = instructorDetail;
}
public Integer getReference() {
return id;
}
public void setReference(Integer id){ this.id = id;}
#Override
public String toString() {
return "Instructor{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", instructorDetail=" + instructorDetail +
'}';
}
}
InstructorDetail -
import javax.persistence.*;
#Table(name="instructor_detail")
#Entity
public class InstructorDetail implements IdentityMarker<Integer>{
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name="youtube_link")
private String youtubeLink;
#Column(name="hobby")
private String hobby;
#OneToOne(cascade = {
CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.PERSIST,
CascadeType.REFRESH
},
mappedBy = "instructorDetail")
// this bi-directional relationship enables us to get the instructor when an instructionDetail is loaded.
private Instructor instructor;
public InstructorDetail(){
}
public InstructorDetail(String youtubeLink, String hobby){
this.youtubeLink = youtubeLink;
this.hobby = hobby;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getYoutubeLink() {
return youtubeLink;
}
public void setYoutubeLink(String youtubeLink) {
this.youtubeLink = youtubeLink;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public Instructor getInstructor() {
return instructor;
}
public void setInstructor(Instructor instructor) {
this.instructor = instructor;
}
public Integer getReference() {
return id;
}
public void setReference(Integer id){ this.id = id;}
#Override
public String toString() {
return "InstructorDetail{" +
"id=" + id +
", youtubeLink='" + youtubeLink + '\'' +
", hobby='" + hobby + '\''+
'}';
}
#PreRemove
private void preRemove() {
System.out.println("pre remove call");
instructor.setInstructorDetail(null);
}
}
Following is the client code
private static void deleteInstructorDetail(){
InstructorDetailDao instructorDetailDao = new InstructorDetailDaoImpl();
InstructorDetail instructorDetail = instructorDetailDao.getInstructorDetail(2);
Instructor instructor = instructorDetail.getInstructor();
System.out.println("Instructor: " + instructor);
boolean b = instructorDetailDao.deleteInstructorDetail(instructorDetail);
assert b == true: "InstructorDetail is not deleted!";
System.out.println("Trying to load Instructor.. It should be deleted!");
InstructorDao instructorDao = new InstructorDaoImpl();
instructor = instructorDao.getInstructor(instructor.getId());
assert instructor != null: "Instructor also got deleted!";
}
Any help would be appreciated! Thanks in advance.

How to update one field in my Entity using JPA Repository

I have Entity with 3 fields: id, lastname and phoneNumber. I want to create method which works for update all fields or only one or two.
I use Hibernate and JPA Repository.
When I try to update all fields everything works well but when for example i want to update only lastname without changing of phoneNumber I have in output null insted of old phoneNumber.
Here is my method from Controller:
#PutMapping("/students/update/{id}")
public String updateStudentById(#ModelAttribute Student student, #ModelAttribute StudentDetails studentDetails,
String lastname, String phoneNumber,
#PathVariable Long id) {
Optional<Student> resultOptional = studentRepository.findById(id);
//Student result =resultOptional.get();
resultOptional.ifPresent((Student result) -> {
result.getStudentDetails().setPhoneNumber(studentDetails.getPhoneNumber()); result.getStudentDetails().setLastname(studentDetails.getLastname());
studentRepository.save(result);
});
return "Student updated";
}
The class for update:
#DynamicUpdate
#Entity
public class StudentDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="lastname")
private String lastname;
#Column(name="phone_number")
private String phoneNumber;
public StudentDetails() {
}
public StudentDetails(Long id, String lastname, String phoneNumber) {
this.id = id;
this.lastname = lastname;
this.phoneNumber = phoneNumber;
}
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 String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
The class which has relation with StudentDetails:
#Entity
#Table(name = "student")
#DynamicUpdate
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
//#OneToMany(mappedBy = "student")
#ManyToMany
#JoinTable(name="course_student",joinColumns = #JoinColumn(name="student_id"),
inverseJoinColumns = #JoinColumn(name="course_id"))
private List<Courses> courses;
#OneToOne(cascade = CascadeType.ALL)
// #JoinColumn(name="studen/_details_id") // with this we have dobule student_details column
private StudentDetails studentDetails;
public List<Courses> getCourses() {
return courses;
}
public void setCourses(List<Courses> courses) {
this.courses = courses;
}
public StudentDetails getStudentDetails() {
return studentDetails;
}
public void setStudentDetails(StudentDetails studentDetails) {
this.studentDetails = studentDetails;
}
// Methods for StudentViewController
public String getLastname(){
return studentDetails.getLastname();
}
public String getPhoneNumber(){
return studentDetails.getPhoneNumber();
}
public Student() {
}
public Student(String name, String email, StudentDetails studentDetails) {
// this.id = id;
this.name = name;
this.email = email;
this.studentDetails = studentDetails;
}
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 String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
'}';
}
}
I was looking for solution and I added #DynamicUpdate but still it doesn't work.
Your code works properly. When you only provide lastName parameter in your request, then the phoneNumber parameter will be mapped to null so you override the phoneNumer property in your entity with this null value.
Change the code in the following way:
resultOptional.ifPresent((Student result) -> {
if(studentDetails.getPhoneNumber()!=null) {
result.getStudentDetails().setPhoneNumber(studentDetails.getPhoneNumber());
}
if(studentDetails.getLastname()!=null) {
result.getStudentDetails().setLastname(studentDetails.getLastname());
}
studentRepository.save(result);
});
Unfortunately it raises an other problem: How will you delete these fields? (How can you set them explicitly to null? )
A possible solution if you check for the "" (empty string) and set the property to null if the parameter is empty string.
It will be a quite messy code anyway...
You should consider using the Spring Data Rest package. It automatically creates all of the standard REST endpoints for your entities and handles all of these PUT/PATCH/POST/DELETE issues out of the box.
why don't you just set the params of your request in you setters?
resultOptional.ifPresent((Student result) -> {
result.getStudentDetails().setPhoneNumber(phoneNumber);
result.getStudentDetails().setLastname(lastname);
studentRepository.save(result);
});
You forget set #OneToOne mapping in StudentDetails - StudentDetails also need field of type Student which will be annotated #OneToOne.
Also you have to ensure, that all of entity fields will be filled - read more about fetch types.

How to make a join on Java with criteria builder

I have a database in which I need to make a Join from Java with a CriteriaBuilder.
I have this code so far:
CriteriaBuilder cb = entman.getCriteriaBuilder();
CriteriaQuery<Company> query = cb.createQuery(Company.class);
Root<Employee> teacher = query.from(Employee.class);
Join<Employee, Company> employees = teacher.join("id");
query.select(employees).where(cb.equal(teacher.get("name"), ""));
List<Company> results = entman.createQuery(query).getResultList();
return results;
After I run this code ( with springboot) i get this error: Cannot join to attribute of basic type
Does anyone know what should I do to make it work ?
PS: I will provide any other information if needed.
Thanks in advance.
company database
employee database
L.E.:
Employee:
#Entity
public class Employee {
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
#Size(min = 1)
private String name;
#Column(nullable = false)
#Temporal(TemporalType.DATE)
private Date hire_date;
#ManyToOne
//#JoinColumn(name = "id")
private Company company;
public Employee() {}
public Employee(Long id, String name, Date date, Company company) {
setId(id);
setName(name);
setHire_date(date);
setCompany(company);
}
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 Date getHire_date() {
return hire_date;
}
public void setHire_date(Date hire_date) {
this.hire_date = hire_date;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
Company:
#Entity
public class Company {
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
#Size(min = 1)
private String name;
#OneToMany(mappedBy="company", cascade = CascadeType.ALL, fetch=FetchType.EAGER, orphanRemoval=true)
private Collection<Employee> employees;
public Company() {}
public Company(Long id, String name) {
setId(id);
setName(name);
}
public Company(Long id, String name, Collection<Employee> employees) {
setId(id);
setName(name);
setEmployees(employees);
}
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 Collection<Employee> getEmployees()
{
return employees;
}
public void setEmployees(Collection<Employee> employees) {
this.employees = employees;
}
#Override
public String toString() {
return "Company [id=" + id + ", name=" + name + ", employees=" + employees.toString() + "]";
}
}
You need to have a Teacher element, not a reference to id because you cannot join a #Column field...
Change id mapping from #Column:
#Column(name = "id")
private Integer id;
with a #ManyToOne (or the needed one) association:
#ManyToOne
#JoinColumn(name = "id")
private Teacher teacher;
After this your join will work as expected.

Caused by: java.sql.SQLSyntaxErrorException: ORA-01722: invalid number in JPA HIBERNATE HQL

Having this class
#Entity
public class PriorityAreaKeyword {
public enum PriorityAreaKey {
ALL ("ALL", "ALL DEVICES"),
IOS ("IOS", "IOS"),
ANDROID ("ANDROID","ANDROID");
private final String name;
private final String id;
private PriorityAreaKey(String name, String id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public String getId() {
return id;
}
}
#Id
private Long id;
#Column(name = "key")
#Enumerated(EnumType.STRING)
private PriorityAreaKey key;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public PriorityAreaKey getKey() {
return key;
}
public void setKey(PriorityAreaKey key) {
this.key = key;
}
public List<PriorityArea> getPriorityAreas() {
return priorityAreas;
}
public void setPriorityAreas(List<PriorityArea> priorityAreas) {
this.priorityAreas = priorityAreas;
}
}
I have in the DAO this method that is working fine:
#Override
#SuppressWarnings("unchecked")
public Set<PriorityArea> findPriorityAreas(PriorityAreaKey key) {
String jpql = "from PriorityAreaKeyword as pak where pak.key = :key";
Query query = entityManager.createQuery(jpql);
query.setParameter("key", key);
List<PriorityArea> priorityAreas = query.getResultList();
return new HashSet<PriorityArea>(priorityAreas);
}
I created a view like this v_report_beneficiary_list (id, email, priority_area_key)
/**
*
*/
#Entity
#Table(name = "v_report_beneficiary_list")
public class ReportBeneficiaryItem {
private Long id;
private String email;
private PriorityAreaKey priorityAreaKey;
/**
* #return the id
*/
#Id
public Long getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
#Column(name = "email")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Column(name = "priority_area_key")
public PriorityAreaKey getPriorityAreaKey() {
return priorityAreaKey;
}
public void setPriorityAreaKey(PriorityAreaKey priorityAreaKey) {
this.priorityAreaKey = priorityAreaKey;
}
In the DAO I've created another method like this:
#Su
ppressWarnings("unchecked")
#Override
public List<ReportBeneficiaryItem> findReportProposalXBeneficiary(ProposalExportFilter filter) {
// Create basic query
String jpql = "from " + ReportBeneficiaryItem.class.getName() + " b where b.priorityAreaKey = :key ";
// Create and execute jpa query
Query query = createQuery(jpql);
query.setParameter("key", filter.getPriorityAreaKey());
return query.getResultList();
}
That throws me a throws me an Exception Caused By: java.sql.SQLSyntaxErrorException: ORA-01722: invalid number
You are missing #Enumerated(EnumType.STRING) on ReportBeneficiaryItem#getPriorityAreaKey() as you have on PriorityAreaKeyword#key, so it's expecting numbers (enum index) in the database for that field, but finds strings
#Column(name = "priority_area_key")
#Enumerated(EnumType.STRING)
public PriorityAreaKey getPriorityAreaKey() {
return priorityAreaKey;
}

Retrieve data using hsql - relation many to one

I define the following entities :BaseEntity , magasin and article :
#Entity(name = "magasin")
#Table(name = "magasin")
public class Magasin extends BaseEntity {
private static final long serialVersionUID = 1L;
#Basic
#Size(min=5, max=100, message="The name must be between {min} and {max} characters")
private String name;
#OneToMany(cascade=CascadeType.ALL, mappedBy="magasin")
#Valid
private Set<Article> article;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Article> getArticle() {
return article;
}
public void setArticle(Set<Article> article) {
this.article = article;
}
}
#Entity(name="article")
#Table(name="article")
public class Article extends BaseEntity {
private static final long serialVersionUID = 1L;
#ManyToOne
private Magasin magasin;
#Basic
#Size(min=5, max=100, message="The name must be between {min} and {max} characters")
private String name;
#Basic
private float price;
public Magasin getMagasin() {
return magasin;
}
public void setMagasin(Magasin magasin) {
this.magasin = magasin;
}
public String getName() {
return name;
}
public void setName(String nom) {
this.name = nom;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public boolean isNew() {
return (this.id == null);
}
}
How can create a hql query in order to retrieve all Article for a magasin selected ?
I try this
#Override
public List<Article> findArticle(Magasin magasin) {
String query = "From Article m where m.magasin.id = "+magasin.getId();
System.out.print(query);
Session session = getSessionFactory().getCurrentSession();
if((session.createQuery(query).list()!=null) && (session.createQuery(query).list().size()!=0))
return (List<Article>) session.createQuery(query).list();
else
return null;
}
But it returns nothing , always null .How can I resolve it ?
I don't know the type of your magasin id so adapt the code below.
First get the Magasin instance for the id:
Magasin mag = (Magasin)session.get(Magasin.class, id);
Next you can access the articles for the magasin mag via accessor
Set<Article> articles = mag.getArticle();
Try this:
"Select * From Article,Mgasin where Article.mgasin.id = "+magasin.getId();

Categories