Failing to insert the foreign key in the owning table when save method is being fired from the non-owning object in a bidirectional relationship? - spring

So I have Two entities 'Student' and 'Parents' and the relationship between them is of 'OneToOne'.
Owning side here is 'student'.
Student.java
import java.util.UUID;
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.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
#Entity
#Table(name="student")
public class Student {
#Id
#Column(name="id")
#GeneratedValue(generator="pg-uuid",strategy=GenerationType.AUTO)
#Type(type = "org.hibernate.type.PostgresUUIDType")
private UUID id;
#Column(name="name")
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="parent_id")
private Parents parents;
Student(){
}
Student(String name){
this.name=name;
}
//Getters And Setters
Parents.java
import java.util.UUID;
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.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
#Entity
#Table(name="parents")
public class Parents {
#Id
#Column(name="id")
#GeneratedValue(generator="pg-uuid",strategy=GenerationType.AUTO)
#Type(type = "org.hibernate.type.PostgresUUIDType")
private UUID id;
#Column(name="father_name")
private String fatherName;
#Column(name="mother_name")
private String motherName;
#OneToOne(mappedBy = "parents",cascade = CascadeType.ALL)
private Student student;
public Parents() {
}
public Parents(String fatherName,String motherName) {
this.fatherName=fatherName;
this.motherName = motherName;
}
//Getter and Setters
Database : POSTGRESQL
Student table
Column | Type | Collation | Nullable | Default
-----------+-----------------------+-----------+----------+--------------------
id | uuid | | not null | uuid_generate_v4()
name | character varying(50) | | |
parent_id | uuid | | |
Indexes:
"student_pkey" PRIMARY KEY, btree (id)
"student_parent_id_key" UNIQUE CONSTRAINT, btree (parent_id)
Foreign-key constraints:
"student_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES parents(id)
Parents table
Column | Type | Collation | Nullable | Default
-------------+-----------------------+-----------+----------+--------------------
id | uuid | | not null | uuid_generate_v4()
father_name | character varying(80) | | not null |
mother_name | character varying(80) | | not null |
Indexes:
"parents_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "student" CONSTRAINT "student_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES parents(id)
Note:-
1.) I'm using uuid_generate_v4() as default in both the tables for auto generating UUID
2.)parent_id in the student table is the foreign key that has reference for parents table primary key;
3.) Both the entities are in a bidirectional relationship.
My StudentApp.java
public class StudentApp {
public static void main(String args[])
{
SessionFactory factory=new Configuration()
.configure()
.addAnnotatedClass(Student.class)
.addAnnotatedClass(Parents.class)
.buildSessionFactory();
Session session=factory.getCurrentSession();
try {
Student student=new Student("Sinchan Nohara");
Parents parents=new Parents("Hiroshi Nohara","Missy Nohara");
student.setParents(parents);
session.beginTransaction();
session.save(student)
session.getTransaction().commit();
}
catch(Exception e){
e.printStackTrace();
}
finally{
session.close();
factory.close();
}
}
}
So if I run the code given below in my StudentApp.java
Student student=new Student("Sinchan Nohara");
Parents parents=new Parents("Hiroshi Nohara","Missy Nohara");
student.setParents(parents);
session.beginTransaction();
session.save(student)
session.getTransaction().commit();
its working fine .
Student table (Owning side)
id | name | parent_id
--------------------------------------+----------------+--------------------------------------
89879cd1-a562-48f3-8632-9eaa5d288f5f | Sinchan Nohara | 82206833-b0d0-4e99-94f5-cab32he8a361
Parents Table (Referenced Side)
id | father_name | mother_name
--------------------------------------+----------------+--------------
82206833-b0d0-4e99-94f5-cab32he8a361 | Hiroshi Nohara | Missy Nohara
But when I try to run the code given below in my StudentApp.java
Student student=new Student("Sinchan Nohara");
Parents parents=new Parents("Hiroshi Nohara","Missy Nohara");
parents.setStudent(student);
session.beginTransaction();
session.save(parents)
session.getTransaction().commit();
then the foreign key(parent_id) in the student table is not getting inserted.
look at the tables below now,
Parents table
id | father_name | mother_name
--------------------------------------+----------------+--------------
380e4daa-0cba-4e26-aa3f-446dbc4fd2c0 | Hiroshi Nohara | Missy Nohara
Student Table
id | name | parent_id
--------------------------------------+----------------+-----------
de062q8d-ff58-4c05-a342-7uu67a4c842d | Sinchan Nohara |
Why this is happening? and how to fix it?

Dealing with bidirectionnal relations with hibernate is tricky. In fact, your model shows that the relation is owned by the student. When you define the relation, it is not enough to set the student of a parents to make it work. You have to set the parents of the student.
This is tricky because when you load a student or a parents you will get both objects with their bidirectional relation properly set.
The problems arise when you do create, update or delete. That's where the owner of the relation becomes important.
By the way, in your case, I would rather set the parents as the owner of the relation to the student since, I imagine, parents may exist without student and not the opposite.

Related

I want to search book(Many) entity from library(One) entity. (A->B, B->A)

It's so working well when I was trying to search "nice_library" from "book". I guess because the nice_library FK is saved on the book.
book = bookRepository.findById(book.getId()).orElseThrow(() -> new RuntimeException());
Long id = book.getNiceLibrary().getId();
assertEquals(id, niceLibrary.getId());
but I can't get anything when I tried to search "book" from "nice_library". I guess because nice_library doesn't have FK of book
niceLibrary = niceLibraryRepository.findById(niceLibrary.getId()).orElseThrow(() -> new RuntimeException());
List<Book> books = niceLibrary.getBooks(); // WHAT THE
I was expecting the "Book.id" to be saved on the "nice_library" table for search to "book" from "nice_library".
Book.java
package com.example.jpa;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
#Getter
#Entity
public class Book {
#Id
#GeneratedValue
#Column
private Long id;
#Setter
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "LIBRARY_ID") // Foreign Key
private NiceLibrary niceLibrary;
}
NiceLibrary.java
package com.example.jpa;
import lombok.Getter;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Getter
#Entity
public class NiceLibrary {
#Id
#GeneratedValue
#Column
private Long id;
#OneToMany(mappedBy = "niceLibrary")
private List<Book> books = new ArrayList<>();
}
book table
id|library_id|
--+----------+
77| 78|
nice_library
id|
--+
78|
Test Code
#SpringBootTest
class BookServiceTest {
#Autowired
private BookRepository bookRepository;
#Autowired
private NiceLibraryRepository niceLibraryRepository;
#Test
public void book() {
Book book = new Book();
NiceLibrary niceLibrary = new NiceLibrary();
niceLibrary.getBooks().add(book);
book.setNiceLibrary(niceLibrary);
bookRepository.save(book);
book = bookRepository.findById(book.getId()).orElseThrow(() -> new RuntimeException());
Long id = book.getNiceLibrary().getId();
assertEquals(id, niceLibrary.getId());
niceLibrary = niceLibraryRepository.findById(niceLibrary.getId()).orElseThrow(() -> new RuntimeException());
List<Book> books = niceLibrary.getBooks(); // ???
}
How can I search book from nice_library?
From what I understand you want to be able to get a specific book from the library.
The one-to-many relationship is setup by having a reference to the library in the book table.
There is no reason to store the book's id in the library table.
This is all you need to setup a one-to-many relationship and you have this correct.
+-----------------+
| Book |
+-----------------+
| id |
| LIBRARY_ID (fk) |
+-----------------+
+------------+
| Library |
+------------+
| id |
+------------+
The way you are describing the problem is that you are expecting a library to only have one book, which is about as big as my library.
In a one-to-many relationship the one side (library in this case) will always have a collection of entities and the other side (book in this case) will only have a reference to a single entity.

why employee_id and department_id not update automatic in database Hibernate jpa

few days back i starting learning hibernate JPA but i am unable to find solution of given problem below
My Project consist three class employee ,phone ,department by seeing code you can easily understand what i am doing .
Main problem raise when i try to save this data into database using spring boot controller it showing null value in column . In employee table department_id is null(not automatic update using cascade.All) same in phone table employee_id is null.
I do not want update manually .is their any way so dep_id and emp_id automatic update to foreign key table .
{
"name":"CSE",
"employees":[
{
"name":"Welcome",
"age":23,
"phones":[{"number":1234567890},{"number":1234567890}]
},
{
"name":"back",
"age":25,
"phones":[{"number":1234567890},{"number":1234567890}]
}
]
}
package com.example.entity;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private int age;
#ManyToOne
#JoinColumn(name = "department_id")
private Department department;
#OneToMany(mappedBy = "employee", cascade=CascadeType.ALL)
private List<Phone> phones;
// getters and setters...
}
package com.example.entity;
import javax.persistence.*
#Entity
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String number;
#ManyToOne
#JoinColumn(name = "employee_id")
private Employee employee;
// getters and setters...
}
package com.example.entity;
import javax.persistence.*;
import java.util.List;
#Entity
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees;
}
dept table in database
id name
1 CSE
employee table
id name age department_id
1. welcome 23. null
2. back. 25. null
phone table
id number employee_id
1. 1234567890. null
2. 1234567890. null
3. 1234567890 null
4. 1234567890. null
Why employee_id and department_id not updating automatic in cascade All
Controller class
package com.example.controller;
import com.example.dao.DepRepo;
import com.example.dao.EmployeeRepo;
import com.example.dao.PhoneRepo;
import com.example.entity.Department;
import com.example.entity.Employee;
import com.example.service.FakeService;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import jakarta.inject.Inject;
#Controller("/dummy")
public class DummyController {
#Inject
FakeService fakeService;
#Inject
PhoneRepo phoneRepo;
#Inject
EmployeeRepo employeeRepo;
#Inject
DepRepo depRepo;
#Get ("/")
public String fun(){
fakeService.fun();
return "welcome back";
}
#Post("/add")
public HttpResponse<?> fun(#Body Department dep){
System.out.println(dep);
depRepo.save(dep);
return HttpResponse.status(HttpStatus.ACCEPTED).body("data add successfully");
}
}
Your Hibernate mapping says that the relationships are mapped by the 'many' side of the association:
#OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees;
So Hibernate looks for the value of the 'department' in the employee entity and it is null (because there's no value in the JSON data)
So try removing mappedBy to tell Hibernate that the relationship is mapped on the 'one' side

Composite primary key vs multiple primary keys

Having this entities:
User.java:
#Entity
#NoArgsConstructor
#Getter
#Setter
public class User {
#Id
private int id;
private String username;
#OneToMany(mappedBy = "owner")
#MapKey(name = "friend_id")
private Map<User, Friendship> friends = new HashMap<>();
}
Friendship:
#Entity
#Data
//#IdClass(Friendship.class)
public class Friendship implements Serializable {
#Id
private int owner_id;
#Id
private int friend_id;
private String level;
#ManyToOne
#MapsId("owner_id")
private User owner;
#ManyToOne
#MapsId("friend_id")
private User friend;
}
I though I must have #IdClass or #EmbeddedId if I want to use two or more primary keys. But as shown above, I could ommit either, and just declare two primary keys (this is what I mean it "compiles"). So the question is, why to even bother using either of those annotations and just declare more keys?
generated table:
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| owner_id | int | NO | PRI | NULL | |
| friend_id | int | NO | PRI | NULL | |
| level | varchar(255) | YES | | NULL | |
+-----------+--------------+------+-----+---------+-------+
As it's mentioned in the hibernate documentation:
The restriction that a composite identifier has to be represented by a "primary key class" (e.g. #EmbeddedId or #IdClass) is only JPA-specific.
Hibernate does allow composite identifiers to be defined without a "primary key class" via multiple #Id attributes.
Although the mapping is much simpler than using an #EmbeddedId or an #IdClass, there’s no separation between the entity instance and the actual identifier. To query this entity, an instance of the entity itself must be supplied to the persistence context.
#Entity
public class Friendship implements Serializable {
/*
It's better to use object wrapper classes instead of the corresponding
primitive types. Because, for example, uninitialized Integer is null,
but uninitialized int is 0 that can be a legal id.
*/
#Id
private Integer ownerId;
#Id
private Integer friendId;
public Friendship() {
}
public Friendship(Integer ownerId, Integer friendId) {
this.ownerId = ownerId;
this.friendId = friendId;
}
// ...
}
Friendship friendship = entityManager.find(Friendship.class, new Friendship(ownerId, friendId));

JPA #ForeignKey(value = ConstraintMode.NO_CONSTRAINT) not working with #ManyToMany

I have two entities with ManyToMany Relationship. Goal here is to create schema when application start with no foreign key
1). Job.java
package com.govjobportalbackend.entity;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name = "job")
public class Job extends BasicEntity {
#Column(name = "icon")
private String icon;
#ManyToMany
#JoinTable(
name="job_city",
joinColumns = #JoinColumn(name = "job_id"),
inverseJoinColumns = #JoinColumn(name = "city_id"),
foreignKey = #ForeignKey(value = ConstraintMode.NO_CONSTRAINT),
inverseForeignKey = #ForeignKey(value = ConstraintMode.NO_CONSTRAINT)
)
private List<City> cities;
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public List<City> getCities() {
return cities;
}
public void setCities(List<City> cities) {
this.cities = cities;
}
}
2). City.java
package com.govjobportalbackend.entity;
import java.util.List;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
#Entity
#DiscriminatorValue(value = "city")
public class City extends JobMetadata {
#ManyToMany(mappedBy = "cities")
private List<Job> jobs;
#Override
public List<Job> getJobs() {
return jobs;
}
#Override
public void setJobs(List<Job> jobs) {
this.jobs = jobs;
}
}
Below property is set in application.properties file
spring.jpa.hibernate.ddl-auto=update
When run the application, it is logging below SQLs in logs and is creating two foreign keys
Hibernate: create table job (id int4 not null, name varchar(255), icon varchar(255), primary key (id))
Hibernate: create table job_city (job_id int4 not null, city_id int4 not null)
Hibernate: create table job_metadata (type varchar(31) not null, id int4 not null, name varchar(255), primary key (id))
Hibernate: alter table if exists job_city add constraint FKiksm0d31mc3osxut4ciaf4uof foreign key (job_id) references job
Hibernate: alter table if exists job_city add constraint FKknw4pf63xt1tvnqrmrjrm5hqq foreign key (city_id) references job_metadata
If I annotate as per below in City.java then it works as expected but as per my "little" research, this bug is fixed in hibernate (so mapped entity is not required to be annotated with depreciated annotation), or may be I am wrong.
#ManyToMany(mappedBy = "cities")
#org.hibernate.annotations.ForeignKey(name = "none")
private List<Job> jobs;
Environment I am using is below;
Java 11
Hibernate 5.4.28.Final (spring-boot-starter-web)
As SimonMartinelli pointed out, this is most definitely a Hibernate bug. The version that worked for me was:
#JoinTable(
name="job_city",
joinColumns = #JoinColumn(name = "job_id", foreignKey = #ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)),
inverseJoinColumns = #JoinColumn(name = "city_id", foreignKey = #ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
)
I found that the functionality breaks when you either (1) use #JoinTable.foreignKey instead, or (2) omit the name parameter.

hibernate #ManyToOne no table relationship

Currently Using:
Hibernate 4.0.1.Final
Spring-data-jpa: 1.0.3.RELEASE
QueryDSL: 2.3.0
MySQL 5.x
I have an interesting problem that I have not found the answer, or clue for yet. I have two tables that did not have foreign key or other relationship. But to try and solve this issue I added one. I want my User entity to hold it's UserRole. This pattern is repeated throughout the database, but this is the easiest to describe.
Here are my tables:
User
userId bigint(20) PK
password varchar(255)
status int(11)
userName varchar(255)
userRoleId long
CONSTRAINT `FK_USERROLE` FOREIGN KEY (`userRoleId`) REFERENCES `UserRole` (`userRoleId`)
UserRole
userRoleId bigint(20) PK
userRoleDescription varchar(255)
userRoleDescriptionShort varchar(255)
Here are my classes:
User.java
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#XmlRootElement(name = "User")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
private String password;
private int status;
private String userName;
#ManyToOne
#JoinColumn(name = "userRoleId")
private UserRole userRole;
public UserRole getUserRole() {
return userRole;
}
public void setUserRole(UserRole userRole) {
this.userRole = userRole;
}
UserRole.java
#Entity
#XmlRootElement(name = "userRole")
public class UserRole {
private Long userRoleId;
private String userRoleDescription;
private String userRoleDescriptionShort;
#ElementCollection
#OneToMany(mappedBy = "userRole")
private List<User> users;
public UserRole() {...}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getUserRoleId() {... }
#OneToMany(mappedBy = "userRole")
public List<User> getUsers() {...}
So you can see where I am trying to associate the UserRole.userRoleId with the User. I thought perhaps Hibernate would build the mapping and retrieve/associate the UserRole whenever the User was updated.
I have gone back and edited this post to use a foreign key between the tables, but on app server startup I get this:
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.List, at table: UserRole, for columns: [org.hibernate.mapping.Column(users)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:304)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:288)
at org.hibernate.mapping.Property.isValid(Property.java:216)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:467)
at org.hibernate.mapping.RootClass.validate(RootClass.java:268)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1287)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1729)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:84)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:904)
I looked for that error, which appears related to JPA and transient variables, which is not the case here.
If i were you i would first clean the annotations, it is forbidden to annotate BOTH getter AND fields in the same entity, it could end up in unexpected results ...
#ElementCollection
#OneToMany(mappedBy = "userRole")
private List<User> users;
#OneToMany(mappedBy = "userRole")
public List<User> getUsers() {...}
should be simplified in :
#ElementCollection
#OneToMany(mappedBy = "userRole")
public List<User> getUsers() {...}

Resources