I have two entities in java (Student and Group) with a #OneToMany relationship
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name="name")
private Integer groupName;
public Group(Integer groupName) {
this.groupName = groupName;
}
#OneToMany
#JoinTable(name = "groups_students",
joinColumns= #JoinColumn(name = "group_id"),
inverseJoinColumns = #JoinColumn(name = "student_id")
)
private Set<Student> students;
}
And I have a third column date in the table groups_students on the localhost. date is not a field in classes. I need to get this in the #Query. Can anyone please help how can I get this (maybe give information to the code about this date column or any other ways to)
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
public Student(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
The way of handling additional attributes from the join table is to define that as a separate entity and then split the relation using the intermediate entity
#Entity
#Table(name = "groups_students")
public class GroupStudent {
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Group group;
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Student student;
#Column(name = "date")
private Date date;
//getter-setter
}
Now modify the relationship on the Group entity
#OneToMany(mappedBy = "group", cascade = CascadeType.ALL)
private List<GroupStudent> groupStudents = new ArrayList<>();
And modify also the Student entity relation
#OneToMany(mappedBy = "student", cascade = CascadeType.ALL)
private List<GroupStudent> groupStudents = new ArrayList<>();
Now if you want to query the additional attribute like the date, you can have a JPQL like this (for example, if you want to get all students of a group where date is in the past)
#Query(SELECT s FROM Student s JOIN s.groupStudents gs WHERE gs.group = :group and gs.date < CURRENT_DATE)
Related
In the following example, there are 3 entities which have relations e.g. #ManyToMany, #OneToMany and #ManyToOne:
Student:
#Entity
#Data
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
private String name;
#JsonIgnore
#ManyToMany(mappedBy = "students")
private Set<Subject> subjects = new HashSet<>();
}
Subject:
#Entity
#Data
public class Subject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
private String name;
#ManyToMany
#JoinTable(
name = "subject_student",
joinColumns = #JoinColumn(name = "subject_id"),
inverseJoinColumns = #JoinColumn(name = "student_id")
)
Set<Student> students = new HashSet<>();
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "teacher_id", referencedColumnName = "id")
private Teacher teacher;
}
Teacher:
#Entity
#Data
public class Teacher {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#JsonIgnore
#OneToMany(mappedBy = "teacher")
private Set<Subject> subjects;
}
1. In the subject entity, I tried to remove #JoinColumn and the related entities are connected as the example above:
#ManyToMany
#JoinTable(name="subject_student")
public Set<Student> students = new HashSet<>();
#ManyToOne(cascade = CascadeType.ALL)
private Teacher teacher;
So, if we want to use subject_id - student_id pair in subject_student table and use teacher_id in subject table as it is created in the example, can I use my simplified notation by removing #JoinColumn? Because, if there is not a special case, I think it is redundant to verbose notation of relations.
2. When I use the second approach, the columns are created as plural e.g. subjects_id - students_id in subject_student. So, can I prevent this and create them as in the previous example by using my approach?
Using Hibernate, I have created two entities - Employee and EmployeeDetails. Since EmployeeDetails cannot exist without a corresponding entry in Employee, I figured I don't need an extra ID for EmployeeDetails, but could instead use the ID of the Employee entity. Here is how I have implemented this idea:
Employee-Entity:
#Entity
#Table(name = "employees")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#OneToOne(cascade = CascadeType.ALL)
EmployeeDetails employeeDetails;
}
Employee-Details-Entity:
#Entity
#Table(name = "employee_details")
#Data
public class EmployeeDetails {
#Id
private Long id;
#Column(name = "address")
private String address;
#Column(name = "e_mail", nullable = false)
private String eMail;
#Column(name = "phone")
private String phone;
#MapsId
#OneToOne(mappedBy = "employeeDetails", cascade = CascadeType.ALL)
#JoinColumn(name = "employee_id")
private Employee employee;
}
By adding the #MapsId annotation to the employee-variable inside EmployeeDetails, I should be assigning the primary key of the Employee-entity to the Id-column of EmployeeDetails.
In a second step, I have written some data into both of my tables.
employee table in MySQL database:
employee_id first_name last_name employee_details_employee_id
1 John Smith null
2 Jennifer Adams null
The last column was somehow generated by Hibernate. I don't understand why. It appears to be some column for identification, but I don't need it.
employee_details table in MySQL database:
employee_id address e_mail phone
1 null john.smith#gmail.com null
2 null jennifer.adams#gmail.com null
I have only assigned an e-mail to the employees. Surprisingly, there is no employee-entry in this database table. I don't really need it anyways, but I was expecting it. So yeah, I think I am doing something terribly wrong and would really appreciate some help.
Change mappedBy side, here useful links
https://vladmihalcea.com/change-one-to-one-primary-key-column-jpa-hibernate/
https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
https://javabydeveloper.com/one-one-bidirectional-association/
#Entity
#Table(name = "employees")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#OneToOne(mappedBy = "employee", cascade = CascadeType.ALL)
EmployeeDetails employeeDetails;
}
Entity
#Table(name = "employee_details")
#Data
public class EmployeeDetails {
#Id
private Long id;
#Column(name = "address")
private String address;
#Column(name = "e_mail", nullable = false)
private String eMail;
#Column(name = "phone")
private String phone;
#MapsId
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "employee_id")
private Employee employee;
}
#MapId is not a popular solution in work with Hibernate.
Maybe in your case, #Embeddable will be a better option?
If I understand correctly, EmployeeDetails cannot exist without correlated Employee. So, EmployeeDetails could be a field in Employee as an embeddable field:
#Entity
#Table(name = "employees")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#Embedded
EmployeeDetails employeeDetails;
}
Then EmployeeDetails doesn't need ID and relation with the employee:
#Embeddable
public class EmployeeDetails {
#Column(name = "address")
private String address;
#Column(name = "e_mail", nullable = false)
private String eMail;
#Column(name = "phone")
private String phone;
}
As you can see, now in the database it's only one table employees, but in our hibernate model, we have two separated objects. Probably you don't need EmployeeDetails without Employee entity, so there is more efficient construction.
If you really need a separated table for EmployeeDetails with relation to Employee I recommend creating standard one-to-one mapping instead of #MapId construction.
I'm building app to learn Hibernate on PostgreSQL. And I'm currently trying to add variable to database that has OneToOne relationship.
First, I create two tables in my database with schema below. On person_detail table it has primary key that's also foreign key to person table.
Then I created two classes, Person and PersonDetail. PersonDetail is child of Person that has OneToOne relationship. And I use code below to add person with personDetail as attribute.
Person person = new Person(
"Rick",
1.7,
dateFromString("1969-4-2"),
new Date()
);
PersonDetail personDetail =
new PersonDetail("myemail#email.com", "Marley");
person.setPersonDetail(personDetail);
session.beginTransaction();
session.save(person);
session.save(personDetail);
session.getTransaction().commit();
System.out.println(person.toString());
But the problem with code above is that Hibernate execute child query first instead of parent query.
Hibernate: insert into person_detail (address, email) values (?, ?)
And since person still empty, we cannot insert any row to person_detail because it violates foreign key constraint.
Is there a way to solve this?
Thanks!
In case some one want to check how I annotate those two classes, I put the code below.
#Entity
#Table(name="person")
#Data
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#Column(name="height")
private double height;
#Column(name="birth_date")
private Date dateBirth;
#Column(name="last_seen")
private Date lastSeen;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id")
private PersonDetail personDetail;
public Person() {}
public Person(String name, double height, Date dateBirth, Date lastSeen){
this.name = name;
this.height = height;
this.dateBirth = dateBirth;
this.lastSeen = lastSeen;
}
}
#Data
#Entity
#Table(name="person_detail")
public class PersonDetail {
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name="email")
private String email;
#Column(name="address")
private String address;
public PersonDetail(){}
public PersonDetail(String email, String address){
this.email = email;
this.address = address;
}
}
I see that you have primary key in table person_details as foreign key to person table, you can use #PrimaryKeyJoinColumn like this:
#Entity
#Table(name="person")
#Data
public class Person {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "height")
private String height;
#OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private PersonDetail personDetail;
public Person() {}
public Person(String name, String height) {
this.name = name;
this.height = height;
}
}
#Data
#Entity
#Table(name="person_detail")
public class PersonDetail {
#Id
#Column(name="id")
private Long id;
#Column(name="email")
private String email;
#OneToOne
#MapsId
#JoinColumn(name = "id", referencedColumnName = "id")
private Person person;
public PersonDetail(){}
public PersonDetail(String email){
this.email = email;
}
}
And if you save your entity, don't forget set Person to PersonDetails:
Person person = new Person("Rick", "1.7");
PersonDetail personDetail = new PersonDetail("myemail#email.com");
personDetail.setPerson(person);
person.setPersonDetail(personDetail);
repository.save(person);
I'm creating a MySQL database as followed :
database design
the Country and Province tables are pre-filled with data. I have the application running and can get stuff no problem, and also the join table person_has_address works when getting.
however, when I insert data using post I want to be able to set the ID of the province, and let spring data jpa just add that number to add_pro_id in the Address table. For example, when I post the following json:
{ "firstName":"bilbo", "lastName":"baggings", "address":{"street":"streetName", "streetNum":3, "zipcode":"1337GG", "city":"TheCity", "province":{"name":"aProvinceName"}} }
jpa should see that aProvinceName exists and grab that id and add that to add_pro_id.
Now it just insert aProvinceName as new value in province and add the new id to add_pro_id.
The person class:
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="per_id")
private int id;
#Column(name="per_name")
private String firstName;
#Column(name="per_surname")
private String lastName;
#Column(name="per_birth_date")
private String birthDate;
#Column(name="per_fax")
private String fax;
#Column(name="per_phone")
private String phone;
#Column(name="per_email")
private String email;
#OneToOne(optional = false, cascade = CascadeType.ALL)
#JoinTable(name="person_has_address", joinColumns = {#JoinColumn(name="pha_per_id", referencedColumnName = "per_id")}, inverseJoinColumns = {#JoinColumn(name="pha_add_id", referencedColumnName = "add_id")})
private Address address;
// getters and setters
This is the person repository:
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findByLastName(#Param("name") String name);
}
This is the address class:
#Entity
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="add_id")
private int id;
#Column(name = "add_street")
private String street;
#Column(name="add_street_num")
private int streetNum;
#Column(name="add_zip")
private String zipcode;
#Column(name="add_city")
private String city;
#JoinColumn(name="add_pro_id", referencedColumnName = "pro_id")
#ManyToOne(optional=false, cascade = CascadeType.ALL)
private Province province;
// getters and setters
Province class:
#Entity
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="pro_id")
private int id;
#Column(name="pro_name")
private String name;
#ManyToOne
#JoinColumn(name="pro_cou_id")
private Country country;
// getters and setters
And lastly country class:
#Entity
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="cou_id", insertable = false, updatable = false)
private int id;
#Column(name="cou_name", insertable = false, updatable = false)
private String name;
// getters and setters
I've tried adding insertable = false and updatable = false, but the application then just inserts NULL values in my database. I've also tried working with #primarykeyjoins, but to no success.
if anyone knows how I should tackle this problem I would much appreciate it!
Thanks in advance.
I have 3 tables. 1 is parent, 1 is student and one is student_parent. This is a many to many relationship between the parent and student. The parentID is from the parent table and the studentID is from the student table. The student_parent table have studentID, parentID and childRelationship. I need to know how to update the childRelationship field using JPA(hibernate).
Note that I'm using Java Spring with the JPA/Hibernate to update my relational database and that there are several other tables but all I wanted to know was to update childRelationship field.
#Entity
#Table(name = "Student")
#Proxy(lazy = false)
public class Student{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "studentID", nullable = false)
private int studentID;
#Column(name="firstName")
private String firstName;
#Column(name="middleName")
private String middleName;
#Column(name="lastName")
private String lastName;
#Column(name="validationID")
private String validationID;
#Column(name="birthDate")
private Date birthDate;
#Column(name="gender")
private char gender;
#Column(name="ethnicity")
private String ethnicity;
#Column(name="photo") // character type has to confirmed
private byte[] photo;
#Column(name="gradeLevel")
private int gradeLevel;
#Column(name="rewardPoints")
private String rewardPoints;
#Column(name="dateCreated")
private Date dateCreated;
#Column(name="schoolID")
private Integer schoolID;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "addressID")
private Address address;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "userName")
private UserAccount userAccount;
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(name="student_parent",
joinColumns = {#JoinColumn(name = "studentID")},
inverseJoinColumns = {#JoinColumn(name = "parentID")})
private Set<Parent> parents = new HashSet<Parent>();
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(name="student_community",
joinColumns = {#JoinColumn(name = "studentID")},
inverseJoinColumns = {#JoinColumn(name = "communityID")})
private Set<Community> communities = new HashSet<Community>();
//getter and setting
then the parent.
#Entity
#Proxy(lazy = false)
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int parentID;
#Column(name="userName")
private String userName;
#Column(name="firstName")
private String firstName;
#Column(name="middleName")
private String middleName;
#Column(name="lastName")
private String lastName;
#Column(name="validationID")
private String validationID;
#Column(name="maritalStatus")
private String maritalStatus;
#Column(name="noOfChildren")
private int noOfChildren;
#Column(name="birthDate")
private Date birthDate;
#Column(name="gender")
private char gender;
#Column(name="ethnicity")
private String ethnicity;
#Column(name="annualIncome")
private Float annualIncome;
#Column(name="dateCreated")
private Date dateCreated;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "addressID")
private Address address;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "userName",unique = true, nullable = false, insertable=false, updatable=false)
private UserAccount userAccount;
//getter and setter
You already have an association table, you need to bring it into the entity world as an association class. The primary key of the association class is a composite key made of the student id and the parent id. Don't forget to implement hashcode and equals on the primary key class. With this mapping you can gain access to the attributes of the m:n relationships (i.e. the "childRelationship" column). You'll also need to add getters and setters or go with field access.
/**
* Maps to parent table in database
*/
#Entity
class Parent {
#Id
long id;
#OneToMany(mappedBy="parent")
Set<Relationship> relationships;
}
/**
* Maps to student table in database
*/
#Entity
class Student {
#Id
long id;
#OneToMany(mappedBy="student")
Set<Relationship> relationships;
}
/**
* Encapsulates primary key in relationship table
*/
class RelationshipId {
long studentId;
long parentId;
#Override
public int hashCode() {
return ((Long)(studentId + parentId)).hashCode();
}
#Override
public boolean equals(Object obj) {
return obj != null
&& obj instanceof RelationshipId
&& ((RelationshipId)obj).studentId == studentId
&& ((RelationshipId)obj).parentId == parentId;
}
}
/**
* Maps to relationship table in database
*/
#Entity
#IdClass(RelationshipId.class)
class Relationship {
#Id
#ManyToOne(optional = false)
#JoinColumn(name="parent")
Parent parent;
#Id
#ManyToOne(optional = false)
#JoinColumn(name="student")
Student student;
#Column
String childRelationship;
}