In a one-to-one relationship, each row in one database table is linked to 1 and only 1 other row in another table.
For example, there is one to one relationship between employee and employee_details.
Types of One to One association
There are two types of one to one association.
a. Unidirectional one to one association
b. Bidirectional one to one association
Unidirectional one to one association
@Entity
@Table(name = "employee_details")
public class EmployeeDetails {
@Id
private int id;
private String departmentName;
.........
.........
}
@Entity
@Table(name = "employee")
public class Employee {
@Id
private int id;
private String name;
@OneToOne
@JoinColumn(name = "employee_details_id")
private EmployeeDetails employeeDetails;
..........
..........
}
Above snippet generates below DDL.
create table employee (
id integer not null,
name varchar(255),
employee_details_id integer,
primary key (id)
)
create table employee_details (
id integer not null,
departmentName varchar(255),
primary key (id)
)
alter table if exists employee
add constraint FKsg6w2kdo2j1t3wt0vxta131it
foreign key (employee_details_id)
references employee_details
In one to one association, employee is considered as client side and employee_details is considered as parent side. But a much more natural mapping would be employee from parent side and pushing the foreign key to employee_details side. This can be achieved using bidirectional one to one association.
Bidirectional one to one association
@Entity
@Table(name = "employee")
public class Employee {
@Id
private int id;
private String name;
@OneToOne(mappedBy = "emp",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY)
private EmployeeDetails employeeDetails;
..........
..........
}
@Entity
@Table(name = "employee_details")
public class EmployeeDetails {
@Id
private int id;
private String departmentName;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "emp_id")
private Employee emp;
..........
..........
}
Above snippet generates below DDL.
create table employee (
id integer not null,
name varchar(255),
primary key (id)
)
create table employee_details (
id integer not null,
departmentName varchar(255),
emp_id integer,
primary key (id)
)
alter table if exists employee_details
add constraint FKjwhhv402ja2w9ioxfbmbouv
foreign key (emp_id)
references employee
Find the below working application.
Step 1: Create new maven project ‘hibernate-bi-directional-one-to-one’.
Step 2: Update pom.xml with maven dependencies.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sample.app</groupId>
<artifactId>hibernate-bi-directional-one-to-one</artifactId>
<version>1</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>15</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.4.1</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.1.2.Final</version>
</dependency>
</dependencies>
</project>
Step 3: Define entity classes.
EmployeeDetails.java
package com.sample.app.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
@Entity
@Table(name = "employee_details")
public class EmployeeDetails {
@Id
private int id;
private String departmentName;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "emp_id")
private Employee emp;
public EmployeeDetails() {
}
public EmployeeDetails(int id, String departmentName, Employee emp) {
this.id = id;
this.departmentName = departmentName;
this.emp = emp;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
public Employee getEmp() {
return emp;
}
public void setEmp(Employee emp) {
this.emp = emp;
}
@Override
public String toString() {
return "EmployeeDetails [id=" + id + ", departmentName=" + departmentName + "]";
}
}
Employee.java
package com.sample.app.entity;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
@Entity
@Table(name = "employee")
public class Employee {
@Id
private int id;
private String name;
@OneToOne(mappedBy = "emp", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
private EmployeeDetails employeeDetails;
public Employee() {
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
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 EmployeeDetails getEmployeeDetails() {
return employeeDetails;
}
public void setEmployeeDetails(EmployeeDetails employeeDetails) {
this.employeeDetails = employeeDetails;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", employeeDetails=" + employeeDetails + "]";
}
}
Step 4: Create hibernate.cfg.xml file under src/main/resources folder.
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database Connection settings -->
<property name="connection.driver_class">org.postgresql.Driver</property>
<property name="connection.url">jdbc:postgresql://127.0.0.1:5432/test</property>
<property name="connection.username">postgres</property>
<property name="connection.password">postgres</property>
<!-- Enable the logging of all the generated SQL statements to the console -->
<property name="show_sql">true</property>
<!-- Format the generated SQL statement to make it more readable, -->
<property name="format_sql">true</property>
<!-- Hibernate will put comments inside all generated SQL statements to
hint what’s the generated SQL trying to do -->
<property name="use_sql_comments">false</property>
<!-- This property makes Hibernate generate the appropriate SQL for the
chosen database. -->
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<!-- mappings for annotated classes -->
<mapping class="com.sample.app.entity.Employee" />
<mapping class="com.sample.app.entity.EmployeeDetails" />
</session-factory>
</hibernate-configuration>
Step 5: Define main application class.
App.java
package com.sample.app;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import com.sample.app.entity.Employee;
import com.sample.app.entity.EmployeeDetails;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
public class App {
private static SessionFactory sessionFactory = buildSessionFactory();
private static <T> List<T> loadAllData(Class<T> clazz, Session session) {
final CriteriaBuilder builder = session.getCriteriaBuilder();
final CriteriaQuery<T> criteria = builder.createQuery(clazz);
criteria.from(clazz);
return session.createQuery(criteria).getResultList();
}
private static SessionFactory buildSessionFactory() {
try {
if (sessionFactory == null) {
StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder()
.configure("hibernate.cfg.xml").build();
Metadata metaData = new MetadataSources(standardRegistry).getMetadataBuilder().build();
sessionFactory = metaData.getSessionFactoryBuilder().build();
}
return sessionFactory;
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static void main(String args[]) {
try (Session session = sessionFactory.openSession()) {
Employee emp = new Employee(1, "Krishna");
session.beginTransaction();
session.persist(emp);
session.flush();
session.getTransaction().commit();
session.beginTransaction();
Employee persistedEmployee = session.find(Employee.class, 1);
EmployeeDetails empDetails1 = new EmployeeDetails(123, "Aerospace", persistedEmployee);
persistedEmployee.setEmployeeDetails(empDetails1);
session.persist(empDetails1);
session.flush();
session.getTransaction().commit();
List<Employee> employees = loadAllData(Employee.class, session);
for (Employee emp1 : employees) {
System.out.println(emp1);
}
}
}
}
Total project structure looks like below.
Run App.java, you will see below messages in the console.
Sep 11, 2022 4:24:03 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.1.2.Final
Sep 11, 2022 4:24:03 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
Sep 11, 2022 4:24:03 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.postgresql.Driver
Sep 11, 2022 4:24:03 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:postgresql://127.0.0.1:5432/test]
Sep 11, 2022 4:24:03 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=postgres}
Sep 11, 2022 4:24:03 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Sep 11, 2022 4:24:03 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
Sep 11, 2022 4:24:03 PM org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl logSelectedDialect
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Hibernate:
alter table if exists employee_details
drop constraint if exists FKjwhhv402ja2w9ioxfbmbouv
Sep 11, 2022 4:24:04 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@439e3cb4] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Sep 11, 2022 4:24:04 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Sep 11, 2022 4:24:04 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: relation "employee_details" does not exist, skipping
Hibernate:
drop table if exists employee cascade
Sep 11, 2022 4:24:04 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Sep 11, 2022 4:24:04 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: table "employee" does not exist, skipping
Hibernate:
drop table if exists employee_details cascade
Sep 11, 2022 4:24:04 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Sep 11, 2022 4:24:04 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: table "employee_details" does not exist, skipping
Hibernate:
create table employee (
id integer not null,
name varchar(255),
primary key (id)
)
Sep 11, 2022 4:24:04 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@521bb1a4] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Hibernate:
create table employee_details (
id integer not null,
departmentName varchar(255),
emp_id integer,
primary key (id)
)
Hibernate:
alter table if exists employee_details
add constraint FKjwhhv402ja2w9ioxfbmbouv
foreign key (emp_id)
references employee
Sep 11, 2022 4:24:04 PM org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
Hibernate:
insert
into
employee
(name, id)
values
(?, ?)
Hibernate:
insert
into
employee_details
(departmentName, emp_id, id)
values
(?, ?, ?)
Hibernate:
select
e1_0.id,
e1_0.name
from
employee e1_0
Hibernate:
select
e1_0.id,
e1_0.departmentName,
e1_0.emp_id
from
employee_details e1_0
where
e1_0.emp_id=?
Employee [id=1, name=Krishna, employeeDetails=EmployeeDetails [id=123, departmentName=Aerospace]]
Query database to confirm the DDL.
test=# \d+ employee;
Table "public.employee"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
id | integer | | not null | | plain | | |
name | character varying(255) | | | | extended | | |
Indexes:
"employee_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "employee_details" CONSTRAINT "fkjwhhv402ja2w9ioxfbmbouv" FOREIGN KEY (emp_id) REFERENCES employee(id)
Access method: heap
test=#
test=#
test=#
test=#
test=# \d+ employee_details;
Table "public.employee_details"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
----------------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
id | integer | | not null | | plain | | |
departmentname | character varying(255) | | | | extended | | |
emp_id | integer | | | | plain | | |
Indexes:
"employee_details_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"fkjwhhv402ja2w9ioxfbmbouv" FOREIGN KEY (emp_id) REFERENCES employee(id)
Access method: heap
Query tables to confirm the data.
test=# SELECT * FROM employee;
id | name
----+---------
1 | Krishna
(1 row)
test=#
test=#
test=# SELECT * FROM employee_details;
id | departmentname | emp_id
-----+----------------+--------
123 | Aerospace | 1
(1 row)
You can download the application from this link.
No comments:
Post a Comment