JPA Fundamentals & Hibernate - 6) One To One Relationship & Cascade Operation
In this article, we are going to learn how to construct a one-to-one relationship between entities in the JPA and when to use Cascade operation Github …
In the OneToMany setup,
Many
sides must have the foreign key and it is the owner of the relatonship
If you only need to see the code, here is the github link
We will have Employee
and Department
tables and Department can have many employee but employee can belong to the one department.
In the one-to-many relationship, many side will have the foreign key. But before implementing with foreign key, let’s look at the what will happen when there is no foreign key:
Run the following sql queries:
CREATE TABLE employee
(
id SERIAL PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE department
(
id SERIAL PRIMARY KEY,
name VARCHAR(100)
);
@Entity
@Table(name = "department")
public class Deparment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
// One Departmant to many employees
@OneToMany
private List<Employee> employees;
// ...
}
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
// ...
}
Create department and employee entities and persist them
public class Main {
public static void main(String[] args) {
Employee employee = new Employee();
employee.setName("Test");
Deparment deparment = new Deparment();
deparment.setName("Department");
deparment.setEmployees(new ArrayList<>());
deparment.getEmployees().add(employee);
try {
entityManager.getTransaction().begin();
entityManager.persist(employee);
entityManager.persist(deparment);
entityManager.getTransaction().commit();
}catch (Exception e) {
System.out.println("Exception: " + e.getMessage());
} finally {
entityManager.close();
}
}
}
If you run the main method, you will get an exception:
Caused by: org.postgresql.util.PSQLException: ERROR: relation "department_employee" does not exist
Here is the sql queries that run by the Hibernate:
Hibernate:
insert
into
employee
(name)
values
(?)
Hibernate:
insert
into
department
(name)
values
(?)
Hibernate:
insert
into
department_employee
(Deparment_id, employees_id)
values
(?, ?)
As you can see, we have additional table called department_employee. If you don’t specify the foreign key in the one-to-many relationship, JPA automatically create a new table to hold the corresponding department and employee ids.
We got an exception, because we don’t want Hibernate update to our database. If you remove the comment related to the hbm2ddl in persistence.xml
, and re-run the project, hibernate will create a new table.
<properties>
uncomment this line
<!-- <property name="hibernate.hbm2ddl.auto" value="update" /> <!– create / create-drop / update –>-->
...
</properties>
In this case, we will have two tables called person & document
and person can have many documents but one document belongs to the only one person.
Please run the following sql queries:
CREATE TABLE person
(
id SERIAL PRIMARY KEY,
name VARCHAR(200)
);
CREATE TABLE document
(
id SERIAL PRIMARY KEY,
name VARCHAR(200),
person_id INT
);
The best way to model one-to-many relationship is to use just @ManyToOne
annotation on the child-side (it is document
in our example)
First let’s create the entities:
@Entity
@Table(name = "person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
// getters and setters
}
@Entity
@Table(name = "document")
public class Document {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "person_id")
private Person person;
// getters and setters
}
First let’s persist one person only:
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.setName("Ozan");
try {
entityManager.getTransaction().begin();
entityManager.persist(person);
entityManager.getTransaction().commit();
}catch (Exception e) {
e.printStackTrace();
} finally {
entityManager.close();
}
}
}
Here is the sql result:
select * from person;
id | name
----+------
1 | Ozan
(1 row)
Now if we want to add document to the person Ozan, we should find the person then add the document:
public class Main {
public static void main(String[] args) {
try {
entityManager.getTransaction().begin();
Person ozan = entityManager.find(Person.class, 1L);
Document document = new Document();
document.setName("test document");
document.setPerson(ozan);
entityManager.persist(document);
entityManager.getTransaction().commit();
}catch (Exception e) {
e.printStackTrace();
} finally {
entityManager.close();
}
}
}
Here are the tables after run the main method:
testdatabase=# select * from person;
id | name
----+------
1 | Ozan
(1 row)
testdatabase=# select * from document;
id | name | person_id
----+---------------+-----------
1 | test document | 1
If we want to find all documents relaton the person Ozan, then we can use the JPQL(The Java Persistence Query Language), after getting the person id.
I will create an article for the JPQL
If possible, please use the best way approach.
You won’t find the bi-directional example in the github repo.
The idea with bidirectional one-to-many association is to allow you to keep a collection of child entities in the parent, and enable you to persist and retrieve the child entities via the parent entity.
@Entity
@Table(name = "person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "person")
private Set<Document> documents = new HashSet<>();
// getters and setters
}
public class Document {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "person_id")
private Person person;
// getters and setters
}
Person person = new Person();
person.setName("Ozan");
// Create a Person
Person person = new Person();
person.setName("test");
// Create Documents
Document doc1 = new Document();
doc1.setName("doc1");
Document doc2 = new Document();
doc2.setName("doc2");
// add documents in the Person
person.getDocuments.add(doc1);
person.getDocuments.add(doc2);
// persist it
entityManager.persist(person);
// Retrieve Post
Person person = entityManager.find(Person.class, 1L);
// Get all the comments
Set<Documents> docs = person.getDocuments()
But this approach has some problems
LazyInitializitonException
Last but not least, wait for the next post …
In this article, we are going to learn how to construct a one-to-one relationship between entities in the JPA and when to use Cascade operation Github …
In this article, we are going to find out how to create composite keys in JPA. Composite key is the key which composed of multiple columns. Github …