Saturday 24 December 2022

Hibernate 6: Composite identifiers with @IdClass

@IdClass annotation specifies a composite primary key class that is mapped to multiple fields or properties of the entity.

 

Step 1: Define a class that contains the composite primary key properties.

public class EmployeePK implements Serializable {
	private Integer empId;
	private Integer deptId;

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

 

Step 2: Specify the composite key class using @IdClass annotation.

@Entity
@Table(name = "employees")
@IdClass(EmployeePK.class)
public class Employee {

	@Id
	private Integer empId;

	@Id
	private Integer deptId;

	private String name;

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

 

The names of the fields or properties in the primary key class and the primary key fields or properties of the entity must correspond and their types must be the same.

 

Find the below working application.

 

Step 1: Create new maven project ‘hibernate-composite-id-with-id-class’.

 

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-composite-id-with-id-class</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 EmployeePK class.

 

EmployeePK.java

package com.sample.app.entity;

import java.io.Serializable;
import java.util.Objects;

public class EmployeePK implements Serializable {
	private Integer empId;
	private Integer deptId;

	public Integer getEmpId() {
		return empId;
	}

	public void setEmpId(Integer empId) {
		this.empId = empId;
	}

	public Integer getDeptId() {
		return deptId;
	}

	public void setDeptId(Integer deptId) {
		this.deptId = deptId;
	}

	@Override
	public int hashCode() {
		return Objects.hash(deptId, empId);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		EmployeePK other = (EmployeePK) obj;
		return Objects.equals(deptId, other.deptId) && Objects.equals(empId, other.empId);
	}

}

 

Step 4: Define Employee entity class.

 

Employee.java

package com.sample.app.entity;

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

@Entity
@Table(name = "employees")
@IdClass(EmployeePK.class)
public class Employee {

	@Id
	private Integer empId;

	@Id
	private Integer deptId;

	private String name;

	public Employee(Integer empId, Integer deptId, String name) {
		this.empId = empId;
		this.deptId = deptId;
		this.name = name;
	}

	public Integer getEmpId() {
		return empId;
	}

	public void setEmpId(Integer empId) {
		this.empId = empId;
	}

	public Integer getDeptId() {
		return deptId;
	}

	public void setDeptId(Integer deptId) {
		this.deptId = deptId;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Employee [empId=" + empId + ", deptId=" + deptId + ", name=" + name + "]";
	}

}

 

Step 5: 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" />

	</session-factory>

</hibernate-configuration>

 

Step 6: 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 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[]) {

		Employee emp1 = new Employee(1, 2, "Krishna");
		Employee emp2 = new Employee(1, 3, "Ram");

		try (Session session = sessionFactory.openSession()) {
			session.beginTransaction();
			session.persist(emp1);
			session.persist(emp2);
			session.flush();
			session.getTransaction().commit();

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

	}
}

 

Total project structure looks like below.



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

Sep 02, 2022 4:45:49 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.1.2.Final
Sep 02, 2022 4:45:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
Sep 02, 2022 4:45:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.postgresql.Driver
Sep 02, 2022 4:45:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:postgresql://127.0.0.1:5432/test]
Sep 02, 2022 4:45:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=postgres}
Sep 02, 2022 4:45:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Sep 02, 2022 4:45:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
Sep 02, 2022 4:45:50 PM org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl logSelectedDialect
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Sep 02, 2022 4:45:51 PM org.hibernate.metamodel.internal.EmbeddableInstantiatorPojoStandard resolveConstructor
INFO: HHH000182: No default (no-argument) constructor for class: com.sample.app.entity.Employee (class must be instantiated by Interceptor)
Sep 02, 2022 4:45:51 PM org.hibernate.metamodel.internal.EntityInstantiatorPojoStandard resolveConstructor
INFO: HHH000182: No default (no-argument) constructor for class: com.sample.app.entity.Employee (class must be instantiated by Interceptor)
Sep 02, 2022 4:45:51 PM org.hibernate.metamodel.internal.EmbeddableInstantiatorPojoStandard resolveConstructor
INFO: HHH000182: No default (no-argument) constructor for class: com.sample.app.entity.Employee (class must be instantiated by Interceptor)
Hibernate: 
    
    drop table if exists employees cascade
Sep 02, 2022 4:45:51 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@48904d5a] 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 (
       deptId integer not null,
        empId integer not null,
        name varchar(255),
        primary key (deptId, empId)
    )
Sep 02, 2022 4:45:51 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@7c72ecc] 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 02, 2022 4:45:51 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
        employees
        (name, deptId, empId) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        employees
        (name, deptId, empId) 
    values
        (?, ?, ?)
Hibernate: 
    select
        e1_0.deptId,
        e1_0.empId,
        e1_0.name 
    from
        employees e1_0
Employee [empId=1, deptId=2, name=Krishna]
Employee [empId=1, deptId=2, name=Krishna]

 

Query PostgreSQL to confirm the same.

test=# \d+ employees;
                                                Table "public.employees"
 Column |          Type          | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
--------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 deptid | integer                |           | not null |         | plain    |             |              | 
 empid  | integer                |           | not null |         | plain    |             |              | 
 name   | character varying(255) |           |          |         | extended |             |              | 
Indexes:
    "employees_pkey" PRIMARY KEY, btree (deptid, empid)
Access method: heap

test=# 
test=# 
test=# SELECT * FROM employees;
 deptid | empid |  name   
--------+-------+---------
      2 |     1 | Krishna
      3 |     1 | Ram
(2 rows)

 

You can download this application from this link.


 

 

 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment