I'm experiencing some difficulties with generating tables in the database through JPA and Hibernate annotations.
When the below code is executed it generates the tables with the following EER diagram.
This is not how I want it to generate the tables.
First of all the relations between the tables are wrong, they need to be OneToOne and not OneToMany.
Secondly, i don't want email to be the primary key in student and teacher.
In Student the ovNumber should be primary key and in Teacher the employeeNumber
I have tried doing it with the #Id annotation but that gives me the following error:
org.hibernate.mapping.JoinedSubclass cannot be cast to org.hibernate.mapping.RootClass
When i try to use #MappedSuperClass the table person does not generate, even when using #Inheritance(strategy=InheritanceType.TABLE_PER_CLASS).
Now my question,
How do I make another variable in subclasses the primary key for the corrosponding table whilst keeping the superclass primary key as foreign key?
How do I fix the relationship between the tables to be an OneToOne relation rather than a OneToMany relation?
Here is an EER diagram of how it should be.
Below are the model classes that are used to generate the tables.
Person.java
#Entity
#Polymorphism(type=PolymorphismType.IMPLICIT)
#Inheritance(strategy=InheritanceType.JOINED)
public class Person implements Comparable<Person>, Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="email", length=64, nullable=false)
private String email;
#Column(name="firstName", length=255)
private String firstName;
#Column(name="insertion", length=255)
private String insertion;
#Column(name="lastName", length=255)
private String lastName;
public Person() {}
/**
* constructor with only email.
*
* #param email
*/
public Person(String email) {
this.email = email;
}
/**
* #param email
* #param firstName
* #param insertion
* #param lastName
*/
public Person(String email, String firstName, String insertion, String lastName){
this.setEmail(email);
this.setFirstName(firstName);
this.setInsertion(insertion);
this.setLastName(lastName);
}
//getters and setters
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getInsertion() {
return insertion;
}
public void setInsertion(String insertion) {
this.insertion = insertion;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Override
public int compareTo(Person o) {
return email.compareTo(o.getEmail());
}
}
Teacher.java
#Entity
#Table(name="teacher")
#PrimaryKeyJoinColumn(name="email", referencedColumnName="email")
public class Teacher extends Person {
private static final long serialVersionUID = 1L;
//this needs to be the pk of teacher table
//#Id
#Column(name="employeeNumber", length=6, nullable=false)
private int employeeNumber;
#Column(name="abbreviation", length=6)
private String abbreviation;
public Teacher(){}
/**
* #param employeeNumber
* #param email
* #param firstName
* #param insertion
* #param lastName
*/
public Teacher(int employeeNumber, String email, String firstName, String insertion, String lastName){
super(email, firstName, insertion, lastName);
this.employeeNumber = employeeNumber;
setAbbreviation();
}
public String getAbbreviation() {
return abbreviation;
}
public void setAbbreviation() {
this.abbreviation = getLastName().substring(0, 4).toUpperCase() + getFirstName().substring(0, 2).toUpperCase();
}
public void setAbbreviation(String abbreviation){
this.abbreviation = abbreviation;
}
public int getEmployeeNumber() {
return employeeNumber;
}
public void setEmployeeNumber(int employeeNumber) {
this.employeeNumber = employeeNumber;
}
#Override
public String toString() {
return "Teacher [abbreviation=" + abbreviation + ", employeeNumber=" + employeeNumber + "]";
}
}
Student.java
#Entity
#Table(name="student")
#PrimaryKeyJoinColumn(name="email", referencedColumnName="email")
public class Student extends Person {
private static final long serialVersionUID = 1L;
#Column(name="cohort")
private int cohort;
//FIXME this needs to be the pk of student table
//#Id
#Column(name="ovNumber", nullable=false)
private int studentOV;
public Student(){}
public Student(int studentOV, int cohort, String email, String firstName,
String insertion, String lastName) {
super(email, firstName, insertion, lastName);
this.studentOV = studentOV;
this.cohort = cohort;
}
public int getCohort() {
return cohort;
}
public void setCohort(int cohort) {
this.cohort = cohort;
}
public int getStudentOV() {
return studentOV;
}
public void setStudentOV(int studentOV) {
this.studentOV = studentOV;
}
#Override
public int compareTo(Person o) {
return getEmail().compareTo(o.getEmail());
}
#Override
public String toString() {
return "Student [firstName=" + getFirstName() + ", insertion=" + getInsertion() + ", lastName=" + getLastName() + ", email="
+ getEmail() + ", cohort=" + getCohort() + ", studentOV=" + getStudentOV() + "]";
}
}
Your goal is to implement inheritance, where Person is your superclass. Teacher and Student are subclasses of that. Inheritance in JPA is not like it's sql-implementation. I advice to read following answer I wrote a while ago. Also read JavaEE 7 - Entity Inheritance Tutorial.
##EDIT##
Here is the solution with different primary keys for each entity for what u asked, still I think that is unusual design (for others please refer to original message):
Person:
#Entity
public class Person implements Serializable {
#Id
#Column
private String email;
#OneToOne(mappedBy = "person")
private Teacher teacher;
#OneToOne(mappedBy = "person")
private Student student;
//more fields
}
Teacher
#Entity
public class Teacher implements Serializable {
#Id
#Column
private Integer employeeNumber;
//constrained to have to be assigned to a Person
//remove constraints if not needed
#OneToOne(optional = false)
#JoinColumn(unique = true, nullable = false)
private Person person;
//more fields
}
Student
#Entity
public class Student implements Serializable {
#Id
#Column
private Integer ovNumber;
//constrained to have to be assigned to a Person
//remove constraints if not needed
#OneToOne(optional = false)
#JoinColumn(unique = true, nullable = false)
private Person person;
//more fields
}
##Original Message##
For your problem I suggest to remodel your jpa-entities. Declare Person as an abstract Entity, extend Teacher and Student by Person.
examplecode:
Person
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "PERSON_TYPE")
public abstract class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Integer id;
//add your needed fields
}
Teacher and Student respectively
#Entity
public class Teacher extends Person {
//no ID needed, it inherits the id of Person
}
try this in Teacher and Student
#OneToOne
#PrimaryKeyJoinColumn(name="person_email", referencedColumnName="email")
private Person preson;
instead of :
#PrimaryKeyJoinColumn(name="email", referencedColumnName="email")
Related
I'm trying to map 2 entities (Course and Student), I have 2 Java classes and 2 MySQL tables, having a ManyToMany relationship. I created the junction table and java class Enrolment (as I want extra information such as the date of enrolment of a student to a course).
I'm trying to insert data using hibernate in this Enrolments table in the MySQL but I keep getting errors. Here are my POJO classes:
Course class:
#Entity
#Table(name = "course")
public class Course {
private int id;
#Column(name = "chapter_id")
private int chapterId;;
#Column(name = "name")
private String title;
#Column(name = "teacher_user_id")
private int teacherId;
#OneToMany(targetEntity=Enrolment.class, mappedBy="course", fetch=FetchType.LAZY)
// #JoinTable(name = "enrolment",
// joinColumns = #JoinColumn(name = "course_id"),
// inverseJoinColumns = #JoinColumn(name = "student_user_id"))
private List<Enrolment> enrolments = new ArrayList<Enrolment>();
public Course(){}
public Course(int id, int chapterId, String title, int teacherId) {
super();
this.id = id;
this.chapterId = chapterId;
this.title = title;
this.teacherId = teacherId;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getChapterId() {
return chapterId;
}
public void setChapterId(int chapterId) {
this.chapterId = chapterId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getTeacherId() {
return teacherId;
}
public void setTeacherId(int teacherId) {
this.teacherId = teacherId;
}
#OneToMany(mappedBy = "course")
public List<Enrolment> getEnrolments() {
return enrolments;
}
public void setEnrolments(List<Enrolment> courses) {
this.enrolments = courses;
}
public void addEnrolment(Enrolment enrolment) {
this.enrolments.add(enrolment);
}
}
Student class (this class is inherited from User parent class, I will attach User Class down below as well. In the database there are different tables as well: User and then Student and Teacher that inherit User parent entity):
#Entity
#Table(name = "student")
#PrimaryKeyJoinColumn(name = "user_id")
#OnDelete(action = OnDeleteAction.CASCADE)
public class Student extends User {
private int grade;
private List<Enrolment> enrolments = new ArrayList<Enrolment>();
public Student(){}
public Student(String fname, String lname, String email, String password, String address, String phone,
int userType, int grade, boolean isAdmin)
{
super(fname, lname, email, password, address, phone, userType, isAdmin);
this.grade=grade;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public void setEnrolments(List<Enrolment> courses) {
this.enrolments = courses;
}
#OneToMany(mappedBy = "student")
public List<Enrolment> getEnrolments() {
return enrolments;
}
public void addCourse(Enrolment course) {
this.enrolments.add(course);
}
public void addEnrolment(Enrolment enrolment) {
this.enrolments.add(enrolment);
}
}
User Class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String firstname;
private String lastname;
private String email;
private String password;
private String address;
private String phone;
#Column(name = "user_type_id")
private int userType;
#Column(name = "is_admin")
private boolean isAdmin;
public User(String fname, String lname, String email, String password, String address, String phone,
int userType, boolean isAdmin) {
//super();
this.firstname = fname;
this.lastname = lname;
this.email = email;
this.password = password;
this.address = address;
this.phone = phone;
this.userType = userType;
this.isAdmin = isAdmin;
}
public User() {}
//getters & setters
And finally this is the Enrolment class:
#Entity
#Table(name = "enrolment")
public class Enrolment {
private int id;
private Student user;
private Course course;
#Column(name = "enrolment_date")
private Date enrolmentDate;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "student_user_id")
public User getUser() {
return user;
}
public void setUser(Student user) {
this.user = user;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "course_id")
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public Date getEnrolmentDate() {
return enrolmentDate;
}
public void setEnrolmentDate(Date enrolmentDate) {
this.enrolmentDate = enrolmentDate;
}
So I'm trying to read a course and a student from database and insert the information in this Enrolment table but it gives errors since trying to read a Course. Here is the DAO method:
#SuppressWarnings("deprecation")
#Transactional
public List<Course> getCoursesOfChapter(int chapterId) {
Configuration con = new Configuration().configure("hibernate.cfg.xml").addAnnotatedClass(Course.class);
SessionFactory sf = con.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from Course where chapter_id = :chapterId");
query.setParameter("chapterId",chapterId);
// List list = query.list();
tx.commit();
return (List<Course>) query.list();
It throws the error at the session factory building:
Exception in thread "main" org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: models.Course.enrolments[models.Enrolment]
at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:1255)
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:808)
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:733)
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:54)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1696)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1664)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:287)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:84)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:474)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:85)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:689)
at dao.CourseDAO.getCourse(CourseDAO.java:52)
at webapp.Main.main(Main.java:132)
Finally, this is my call:
CourseDAO cdao = new CourseDAO();
Course course = cdao.getCourse(1);
I've tried playing with the annotations, make them ManyToMany instead of ManyToOne. I tried to map the User class instead of Student but still didn't work. I tried to make it without the junction class of Enrolment and just generate it without having an actual class for it but still didn't work (as I had to work with 2 session.save() methods one after the other which also gave some error that I couldn't solve). Probably it's a small thing that I'm missing here but I just can't figure it out, sorry for too long code but I really need to solve it fast. If you read through here, I really thank you!
So my question is: Am I missing something here from these mappings and annotations or I should change the structure of my classes?
Boiling down a problem to the bare minimum greatly helps others help you. Here are simpler versions of your student, course and enrollment classes that can be unit tested easily. The many-to-many association between course and student is separated into two many-to-one associations from Enrollment. Note that the associations are bidirectional and that the many side is mapped by the one side. Student cascades persistence operations to Enrollment, which reflects how schools normally work: students enroll in courses, not the other way around.
Course.java
#Entity
public class Course {
#Id
#GeneratedValue
private Long id;
private String title;
#OneToMany(mappedBy = "course")
private List<Enrollment> enrollments;
Course() {
}
Course(String title) {
this.title = title;
this.enrollments = new ArrayList<>();
}
void add(Enrollment enrollment) {
enrollments.add(enrollment);
}
Long getId() {
return id;
}
List<Enrollment> getEnrollments() {
return enrollments;
}
}
Student.java
#Entity
public class Student {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "student", cascade = ALL, orphanRemoval = true)
private List<Enrollment> enrollments;
Student() {
}
Student(String name) {
this.name = name;
this.enrollments = new ArrayList<>();
}
void enroll(Course course) {
enrollments.add(new Enrollment(course, this));
}
}
Enrollment.java
#Entity
public class Enrollment {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private Course course;
#ManyToOne
private Student student;
Enrollment() {
}
Enrollment(Course course, Student student) {
this.course = course;
this.student = student;
course.add(this);
}
}
The test case below checks that the entities are mapped and associated correctly. You can run it with Spring Boot.
SchoolTest.java
#RunWith(SpringRunner.class)
#SpringBootTest
#Transactional
public class SchoolTest {
#Autowired
private CourseRepository courseRepository;
#Autowired
private StudentRepository studentRepository;
#Test
public void run() {
Course course = courseRepository.save(new Course("cs_101"));
int studentCount = 3;
for (int i = 1; i <= studentCount; i++) {
Student student = new Student("student_" + i);
student.enroll(course);
studentRepository.save(student);
}
// push changes to the database and clear the existing entities
// to make the subsequent operations load from the database
entityManager.flush();
entityManager.clear();
Optional<Course> savedCourse = courseRepository.findById(course.getId());
assertTrue(savedCourse.isPresent());
assertEquals(studentCount, savedCourse.get().getEnrollments().size());
}
}
As the warning said, your Enrollment is not registered in Hibernate. If you really don't need it. Please use transient annotation. read more here
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.
This question already has answers here:
Hibernate: duplicate key value violates unique constraint
(4 answers)
Closed 6 years ago.
EDIT:How is this question different from the one linked?
I think this question is different because it seems as if it is caused by JPA trying to add another user with the same id, because of a foregin key value in the class (Student) being added. The issue linked seems to be caused by not generateing the ids automatically.
I have a method that creates a User and returns a User. I pass this User to another method to create a Student. The user is a student. But I can't do this because I get :
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "pk_user_id"
Detail: Key (user_id)=(7001) already exists.
My methods in the backing bean looks like this:
public Users2 addUser(String username, String password, String emailadress,
String firstname, String lastname) {
Users2 u = new Users2();
u.setUsername(username);
u.setPassword(password);
u.setEmailaddress(emailadress);
u.setFirstname(firstname);
u.setLastname(lastname);
System.out.println(em + ": Adding course " + u);
em.persist(u);
em.flush();
System.out.println(u.getUser_id());
return u;
}
public void addStudent(Users2 u2) {
Student s = new Student();
s.setUser_id(u2.getUser_id());
s.setUsername(u2.getUsername());
s.setLastname(u2.getLastname());
s.setFirstname(u2.getFirstname());
s.setPassword(u2.getPassword());
s.setEmailaddress((u2.getEmailaddress()));
em.persist(s);
}
My method in the Jsf bean looks like this:
#Inject
DbStore store;
public String CreateUser(){
long usrid;
String username = this.username;
String password = this.password;
String emailadress = this.emailaddress;
String firstname = this.firstname;
String lastname = this.lastname;
Users2 u1 = store.addUser(username, password, emailadress, firstname, lastname);
//System.out.println(usrid);
String role = this.role;
if(this.role.equals("Student"))
store.addStudent(u1);
return "admin_listcourses.xhtml";
}
My entities:
import javax.persistence.Id;
import javax.validation.constraints.Max;
import java.util.List;
import java.util.Set;
#Entity
#Table(name = "student")
#SecondaryTable(name = "users2", pkJoinColumns=#PrimaryKeyJoinColumn(name="user_id", referencedColumnName="user_id"))
public class Student {
/**
* Created by Elev1 on 2016-08-25.
*
*/
#Id
#SequenceGenerator(name="student_student_id_seq",
sequenceName="student_student_id_seq",
allocationSize=1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator="seq")
#Column(name = "student_id", updatable=false)
private long student_id;
#Column(table="users2", name="username")
private String username;
#Column(table="users2", name="firstname")
private String firstname;
#Column(table="users2", name="lastname")
private String lastname;
#Column(table="users2", name="password")
private String password;
#Column(table="users2", name="emailaddress")
private String emailaddress;
#Column(table="users2", name="user_id")
private long user_id;
#ManyToMany
#JoinTable(name="student_course",
joinColumns=
#JoinColumn(name="student_id", referencedColumnName="student_id"),
inverseJoinColumns=
#JoinColumn(name="course_id", referencedColumnName="course_id")
)
// public List<Course> getCourses() { return courses ; }
public List<Course> courses;
//Getters and setters
public long getStudent_id() {
return student_id;
}
public void setStudent_id(long student_id) {
this.student_id = student_id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
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 List<Course> getCourses() {
return courses;
}
public void setCourses(List<Course> courses) {
this.courses = courses;
}
public long getUser_id() {
return user_id;
}
public void setUser_id(long user_id) {
this.user_id = user_id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmailaddress() {
return emailaddress;
}
public void setEmailaddress(String emailaddress) {
this.emailaddress = emailaddress;
}
}
package se.lexicon.entities;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
#Entity
public class Users2{
// ***********************
// ** Attributes **
// ***********************
#Id
#SequenceGenerator(name="users_user_id_seq",
sequenceName="users_user_id_seq",
allocationSize=1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator="seq")
private Long user_id;
#Column(name = "username", length = 64)
private String username;
private String password;
#Column(name = "emailaddress", length = 64)
private String emailaddress;
#Column(name = "firstname")
private String firstname;
#Column(name = "lastname")
private String lastname;
#Temporal(TemporalType.TIMESTAMP)
private Date last_login;
// ************************
// ** Constructors **
// ************************
// public User() {
// public User(Long user_id) {
// this.user_id = user_id;
// }
// public User(Long user_id, String username, String password, String emailaddress, ??? last_login) {
// this.user_id = user_id;
// this.username = username;
// this.password = password;
// this.emailaddress = emailaddress;
// this.last_login = last_login;
//}
// ******************************
// ** Getters & Setters **
// ******************************
public long getUser_id() {
return user_id;
}
public void setUser_id(long user_id) {
this.user_id = user_id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmailaddress() {
return emailaddress;
}
public void setEmailaddress(String emailaddress) {
this.emailaddress = emailaddress;
}
public Date getLast_login() {
return last_login;
}
public void setLast_login(Date last_login) {
this.last_login = last_login;
}
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;
}
//}
}
EDIT: My solution. I removed the foregin_key constraint in the student table. I only kept student_id, course_id and user_id in the Student class. I removed all connections between Student and Users2 in the Student class, instead I use methods to get those from the Users2 class if a student_id is given. When a user is created, that is a Student, then a Student is added with the user_id of the user set as the user_id of the Student.
Now this isn't a very good solution, so if some one can solve my original problem I would be happy to accept the solution. But for now my solution will have to do.
When you persist a Student instance JPA will also create a row in the Users2 table with the user_id as foreign key to the row in the Student table. But there is already a row with the very same ID in the Users2, as you persisted a Users2 instance just before. That's the reason, why you are facing this SQLException.
To me it does not make much sense to use the SecondaryTable-approach here, as there might me Users which are no Students at all, right? But in your current model the Users table is storing a Foreign Key to the Student table..
In this case here it seems to be more appropriate to use inheritance (or maybe some composition) here, instaead of the SecondaryTable-approach.
A Student is-a User, so Student may inherit from User.
You will find a first overview how to define the mapping in this case here: https://en.wikibooks.org/wiki/Java_Persistence/Inheritance
So I am having a little trouble trying to get my create user function to work. I am trying to create a new user for every new user entry from the Scanner console, but my Hibernate is updating the same person ID, instead of assigning a new person ID to the new user.
my DAO class:
public void createDoctor(Doctor doctor) {
sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(doctor);
session.getTransaction().commit();
session.close();
sessionFactory.close();
}
my main class:
int adminNum = sc.nextInt();
if (adminNum == 1) {
System.out.print("Please enter doctor's first name: ");
sc.nextLine();
String firstName = sc.nextLine();
System.out.print("Please enter doctor's last name: ");
String lastName = sc.nextLine();
Doctor d = new Doctor();
d.setFirstName(firstName);
d.setLastName(lastName);
doctorList.add(d);
int index = doctorList.indexOf(d);
dao.createDoctor(doctorList.get(index));
}
Doctor Class:
import java.util.*;
import javax.persistence.*;
#Entity
public class Doctor extends Person {
#OneToOne
#JoinColumn(name = "SPECIALTY_ID")
private Specialty specialty;
#OneToMany(mappedBy = "doctor", targetEntity = Patient.class,fetch=FetchType.EAGER, cascade= CascadeType.ALL )
private List<Patient> patients;
private double salary;
public Doctor(){
patients = new ArrayList<Patient>();
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSpecialty(Specialty specialty) {
this.specialty = specialty;
}
public Specialty getspecialty() {
return specialty;
}
public void setPatient(Patient patient){
patients.add(patient);
}
public List<Patient> getPatients(){
return patients;
}
}
Person Class:
package edu.cs157b.medicalSystem;
import javax.persistence.*;
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "PERSON_ID")
private int personId;
private String first_name;
private String last_name;
private char sex;
public Person() {
}
public int getPersonId() {
return personId;
}
public void setPersonId(int personId) {
this.personId = personId;
}
public void setFirstName(String first_name) {
this.first_name = first_name;
}
public String getFirstName() {
return first_name;
}
public void setLastName(String last_name) {
this.last_name = last_name;
}
public String getLastName() {
return last_name;
}
public void setSex(char sex){
this.sex = sex;
}
public char getSex(){
return sex;
}
}
In your Person class declare the id field as Integer so that newly created instances will have null value as personId. The primitive int type will always have 0 as initial value.
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "PERSON_ID")
private Integer personId;
I don't like this code much.
Personally, I'd prefer having a ctor that did something rather than a do-nothing default ctor and having to call setters.
I'd persist the Doctor, then add it to the List.
Is this synchronized? Doesn't look thread safe if that List is shared, mutable data.
I see no equals or hashCode implemented here. That might explain it. Best to use the primary key fields to determine equality.
I think you're desperately in need of a tutorial. Forget your assignment for a moment and do something simple:
http://docs.jboss.org/hibernate/core/3.3/reference/en-US/html/tutorial.html
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