Sunday, 25 December 2022

Hibernate 6: Unidirectional One to Many association example

One to Many association links a parent entity with one or more child entities.

 

For example, an employee has one or more bank accounts.

 

There are two types of One to Many associations.

a.   Unidirectional one to many association: It do not have mirroring @ManyToOne (many to one) association on child entity.

b.   Bidirectional one to many association: It has mirroring @ManyToOne (many to one) association on child entity.

 

Unidirectional one to many association example

@Entity
@Table(name = "bank_account_details")
public class BankAccount {

	@Id
	private int id;

	private String accountNumber;

	private String branch;

	private String ifscCode;

	private String address;

	..........
	..........
}

@Entity
@Table(name = "employees")
public class Employee {
	@Id
	private int id;

	private String firstName;

	private String lastName;

	@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
	private Set<BankAccount> bankAccounts = new HashSet();

	...........
	...........
}

Above snippet generates below DDL.

    create table bank_account_details (
        id integer not null,
        accountNumber varchar(255),
        address varchar(255),
        branch varchar(255),
        ifscCode varchar(255),
        primary key (id)
    )

    create table employees (
        id integer not null,
        firstName varchar(255),
        lastName varchar(255),
        primary key (id)
    )
    
    create table employees_bank_account_details (
        Employee_id integer not null,
        bankAccounts_id integer not null,
        primary key (Employee_id, bankAccounts_id)
    )

Follow below step-by-step procedure to build the application.

 

Step 1: Create new maven project ‘hibernate-uni-directional-one-to-many’.

 

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-uni-directional-one-to-many</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

 

BankAccount.java

package com.sample.app.entity;

import java.util.Objects;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "bank_account_details")
public class BankAccount {

	@Id
	private int id;

	private String accountNumber;

	private String branch;

	private String ifscCode;

	private String address;

	public String getAccountNumber() {
		return accountNumber;
	}

	public void setAccountNumber(String accountNumber) {
		this.accountNumber = accountNumber;
	}

	public String getBranch() {
		return branch;
	}

	public void setBranch(String branch) {
		this.branch = branch;
	}

	public String getIfscCode() {
		return ifscCode;
	}

	public void setIfscCode(String ifscCode) {
		this.ifscCode = ifscCode;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	@Override
	public String toString() {
		return "BankAccount [id=" + id + ", accountNumber=" + accountNumber + ", branch=" + branch + ", ifscCode="
				+ ifscCode + ", address=" + address + "]";
	}

	@Override
	public int hashCode() {
		return Objects.hash(id);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		BankAccount other = (BankAccount) obj;
		return id == other.id;
	}

}

Employee.java

package com.sample.app.entity;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;

@Entity
@Table(name = "employees")
public class Employee {
	@Id
	private int id;

	private String firstName;

	private String lastName;

	@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
	private Set<BankAccount> bankAccounts = new HashSet<>();

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	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 Set<BankAccount> getBankAccounts() {
		return bankAccounts;
	}

	public void setBankAccounts(Set<BankAccount> bankAccounts) {
		this.bankAccounts = bankAccounts;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", bankAccounts="
				+ bankAccounts + "]";
	}

	@Override
	public int hashCode() {
		return Objects.hash(id);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Employee other = (Employee) obj;
		return id == other.id;
	}

}

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.BankAccount" />

	</session-factory>

</hibernate-configuration>

Step 5: Define main application class.

 

App.java

package com.sample.app;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

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.BankAccount;
import com.sample.app.entity.Employee;

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()) {

			session.beginTransaction();

			BankAccount account1 = new BankAccount();
			account1.setAccountNumber("number1");
			account1.setAddress("address1");
			account1.setBranch("branch1");
			account1.setId(1);
			account1.setIfscCode("ifsc1");

			BankAccount account2 = new BankAccount();
			account2.setAccountNumber("number2");
			account2.setAddress("address2");
			account2.setBranch("branch2");
			account2.setId(2);
			account2.setIfscCode("ifsc2");

			session.persist(account1);
			session.persist(account2);

			session.getTransaction().commit();

			session.beginTransaction();

			Employee emp1 = new Employee();
			emp1.setFirstName("Krishna");
			emp1.setLastName("Gurram");
			emp1.setId(1);

			Set<BankAccount> bankAccounts = new HashSet<>();
			BankAccount persistedAccount1 = session.find(BankAccount.class, 1);
			BankAccount persistedAccount2 = session.find(BankAccount.class, 2);
			bankAccounts.add(persistedAccount1);
			bankAccounts.add(persistedAccount2);
			emp1.setBankAccounts(bankAccounts);

			session.persist(emp1);
			session.flush();
			session.getTransaction().commit();

			List<Employee> emps = loadAllData(Employee.class, session);
			for (Employee empTemp : emps) {
				System.out.println(empTemp);
			}

		}

	}
}

Total project structure looks like below.




Run App.java, you will see below messages in the console.

Sep 06, 2022 11:42:06 AM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.1.2.Final
Sep 06, 2022 11:42:06 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
Sep 06, 2022 11:42:06 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.postgresql.Driver
Sep 06, 2022 11:42:06 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:postgresql://127.0.0.1:5432/test]
Sep 06, 2022 11:42:06 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=postgres}
Sep 06, 2022 11:42:06 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Sep 06, 2022 11:42:06 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
Sep 06, 2022 11:42:06 AM org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl logSelectedDialect
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Hibernate: 
    
    alter table if exists employees_bank_account_details 
       drop constraint if exists FK6o5c3tk433vu65rv7v4tqmkh9
Sep 06, 2022 11:42:07 AM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@355c94be] 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 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Sep 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: relation "employees_bank_account_details" does not exist, skipping
Hibernate: 
    
    alter table if exists employees_bank_account_details 
       drop constraint if exists FKc19v8493c69m7ogrrx6mnlk8t
Sep 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Sep 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: relation "employees_bank_account_details" does not exist, skipping
Hibernate: 
    
    drop table if exists bank_account_details cascade
Sep 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Sep 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: table "bank_account_details" does not exist, skipping
Hibernate: 
    
    drop table if exists employees cascade
Sep 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Sep 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: table "employees" does not exist, skipping
Hibernate: 
    
    drop table if exists employees_bank_account_details cascade
Sep 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Sep 06, 2022 11:42:07 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: table "employees_bank_account_details" does not exist, skipping
Hibernate: 
    
    create table bank_account_details (
       id integer not null,
        accountNumber varchar(255),
        address varchar(255),
        branch varchar(255),
        ifscCode varchar(255),
        primary key (id)
    )
Sep 06, 2022 11:42:07 AM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@51a18b21] 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 employees (
       id integer not null,
        firstName varchar(255),
        lastName varchar(255),
        primary key (id)
    )
Hibernate: 
    
    create table employees_bank_account_details (
       Employee_id integer not null,
        bankAccounts_id integer not null,
        primary key (Employee_id, bankAccounts_id)
    )
Hibernate: 
    
    alter table if exists employees_bank_account_details 
       add constraint UK_igrdb89e2v09omca09xtlkxcf unique (bankAccounts_id)
Hibernate: 
    
    alter table if exists employees_bank_account_details 
       add constraint FK6o5c3tk433vu65rv7v4tqmkh9 
       foreign key (bankAccounts_id) 
       references bank_account_details
Hibernate: 
    
    alter table if exists employees_bank_account_details 
       add constraint FKc19v8493c69m7ogrrx6mnlk8t 
       foreign key (Employee_id) 
       references employees
Sep 06, 2022 11:42:07 AM 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
        bank_account_details
        (accountNumber, address, branch, ifscCode, id) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        bank_account_details
        (accountNumber, address, branch, ifscCode, id) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        employees
        (firstName, lastName, id) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        employees_bank_account_details
        (Employee_id, bankAccounts_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        employees_bank_account_details
        (Employee_id, bankAccounts_id) 
    values
        (?, ?)
Hibernate: 
    select
        e1_0.id,
        e1_0.firstName,
        e1_0.lastName 
    from
        employees e1_0
Employee [id=1, firstName=Krishna, lastName=Gurram, bankAccounts=[BankAccount [id=1, accountNumber=number1, branch=branch1, ifscCode=ifsc1, address=address1], BankAccount [id=2, accountNumber=number2, branch=branch2, ifscCode=ifsc2, address=address2]]]

 

Query the database to get DDL information.

test=# \d+ employees;
                                                 Table "public.employees"
  Column   |          Type          | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
-----------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id        | integer                |           | not null |         | plain    |             |              | 
 firstname | character varying(255) |           |          |         | extended |             |              | 
 lastname  | character varying(255) |           |          |         | extended |             |              | 
Indexes:
    "employees_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "employees_bank_account_details" CONSTRAINT "fkc19v8493c69m7ogrrx6mnlk8t" FOREIGN KEY (employee_id) REFERENCES employees(id)
Access method: heap

test=# 
test=# \d+ bank_account_details;
                                              Table "public.bank_account_details"
    Column     |          Type          | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
---------------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id            | integer                |           | not null |         | plain    |             |              | 
 accountnumber | character varying(255) |           |          |         | extended |             |              | 
 address       | character varying(255) |           |          |         | extended |             |              | 
 branch        | character varying(255) |           |          |         | extended |             |              | 
 ifsccode      | character varying(255) |           |          |         | extended |             |              | 
Indexes:
    "bank_account_details_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "employees_bank_account_details" CONSTRAINT "fk6o5c3tk433vu65rv7v4tqmkh9" FOREIGN KEY (bankaccounts_id) REFERENCES bank_account_details(id)
Access method: heap

test=# 
test=# 
test=# \d+ employees_bank_account_details
                                  Table "public.employees_bank_account_details"
     Column      |  Type   | Collation | Nullable | Default | Storage | Compression | Stats target | Description 
-----------------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
 employee_id     | integer |           | not null |         | plain   |             |              | 
 bankaccounts_id | integer |           | not null |         | plain   |             |              | 
Indexes:
    "employees_bank_account_details_pkey" PRIMARY KEY, btree (employee_id, bankaccounts_id)
    "uk_igrdb89e2v09omca09xtlkxcf" UNIQUE CONSTRAINT, btree (bankaccounts_id)
Foreign-key constraints:
    "fk6o5c3tk433vu65rv7v4tqmkh9" FOREIGN KEY (bankaccounts_id) REFERENCES bank_account_details(id)
    "fkc19v8493c69m7ogrrx6mnlk8t" FOREIGN KEY (employee_id) REFERENCES employees(id)
Access method: heap

 

Query the tables to view the data.

test=# SELECT * FROM employees;
 id | firstname | lastname 
----+-----------+----------
  1 | Krishna   | Gurram
(1 row)

test=# 
test=# 
test=# SELECT * FROM bank_account_details;
 id | accountnumber | address  | branch  | ifsccode 
----+---------------+----------+---------+----------
  1 | number1       | address1 | branch1 | ifsc1
  2 | number2       | address2 | branch2 | ifsc2
(2 rows)

test=# 
test=# 
test=# 
test=# SELECT * FROM employees_bank_account_details;
 employee_id | bankaccounts_id 
-------------+-----------------
           1 |               1
           1 |               2
(2 rows)

 

 

You can download this application from below link.

https://github.com/harikrishna553/java-libs/tree/master/hibernate6/hibernate-uni-directional-one-to-many

 

 

 

 

 

 





 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment