When you use simple identifiers in an entity, then you can auto generate the identifiers.
How to specify that this identifier is auto generated?
Annotate the identifier with @GeneratedValue annotation.
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
‘strategy’ attribute specifies the logic to generate identifiers. At the time of writing this post, Hibernate support 4 identifier generation strategies.
a. AUTO: Indicates that the persistence provider should pick an appropriate strategy for the particular database.
b. IDENTITY: Indicates that the persistence provider must assign primary keys for the entity using a database identity column.
c. SEQUENCE: Indicates that the persistence provider must assign primary keys for the entity using a database sequence.
d. TABLE: Indicates that the persistence provider must assign primary keys for the entity using an underlying database table to ensure uniqueness.
Unnamed table generator
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Integer id;
..........
.........
}
Since in the above example, I do not specify any table name to generate identifiers, so Hibernate assumes an implicit name of hibernate_sequences.
create table hibernate_sequences (
sequence_name varchar(255) not null,
next_val bigint,
primary key (sequence_name)
)
Configure table identifier using @TableGenerator annotation
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(
strategy = GenerationType.TABLE,
generator = "my-table-generator"
)
@TableGenerator(
name = "my-table-generator",
table = "my_table_identifier",
pkColumnName = "table_name",
valueColumnName = "record_id",
allocationSize = 5
)
private Integer id;
.......
.......
}
Above snippet generate below table to generate identifiers.
create table my_table_identifier (
table_name varchar(255) not null,
record_id bigint,
primary key (table_name)
)
You can use the same identifier table for more than one entity.
@Entity
@Table(name = "persons")
public class Person {
@Id
@GeneratedValue(
strategy = GenerationType.TABLE,
generator = "my-table-generator"
)
@TableGenerator(
name = "my-table-generator",
table = "my_table_identifier",
pkColumnName = "table_name",
valueColumnName = "record_id",
allocationSize = 5
)
private Integer id;
.........
.........
}
Follow below step-by-step procedure to build complete application.
Step 1: Create new maven project ‘hibernate-table-strategy-demo’.
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-table-strategy-demo</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.
Employee.java
package com.sample.app.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.TableGenerator;
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(
strategy = GenerationType.TABLE,
generator = "my-table-generator"
)
@TableGenerator(
name = "my-table-generator",
table = "my_table_identifier",
pkColumnName = "table_name",
valueColumnName = "record_id",
allocationSize = 5
)
private Integer id;
private String name;
public Employee(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + "]";
}
}
Person.java
package com.sample.app.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.TableGenerator;
@Entity
@Table(name = "persons")
public class Person {
@Id
@GeneratedValue(
strategy = GenerationType.TABLE,
generator = "my-table-generator"
)
@TableGenerator(
name = "my-table-generator",
table = "my_table_identifier",
pkColumnName = "table_name",
valueColumnName = "record_id",
allocationSize = 5
)
private Integer id;
private String name;
public Person(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
}
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.Person" />
</session-factory>
</hibernate-configuration>
Step 5: Define main application class.
App.java
package com.sample.app;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.Person;
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[]) {
Map<String, Object> config = new HashMap<>();
config.put("maxThreads", 10);
config.put("storagePath", "/Users/krishna/my-docs");
Employee emp1 = new Employee("Krishna");
Employee emp2 = new Employee("Ram");
Person p1 = new Person("Bhadri");
Person p2 = new Person("Venkata");
try (Session session = sessionFactory.openSession()) {
session.beginTransaction();
session.persist(emp1);
session.persist(emp2);
session.persist(p1);
session.persist(p2);
session.flush();
session.getTransaction().commit();
List<Employee> emps = loadAllData(Employee.class, session);
for (Employee emp : emps) {
System.out.println(emp);
}
List<Person> persons = loadAllData(Person.class, session);
for (Person person : persons) {
System.out.println(person);
}
}
}
}
Total project structure looks like below.
Run App.java, you will see below messages in the console.
Aug 23, 2022 4:02:14 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.1.2.Final
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.postgresql.Driver
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:postgresql://127.0.0.1:5432/test]
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=postgres}
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl logSelectedDialect
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Aug 23, 2022 4:02:15 PM org.hibernate.id.enhanced.TableGenerator determineDefaultSegmentValue
INFO: HHH000398: Explicit segment value for id generator [my_table_identifier.table_name] suggested; using default [employees]
Aug 23, 2022 4:02:15 PM org.hibernate.id.enhanced.TableGenerator determineDefaultSegmentValue
INFO: HHH000398: Explicit segment value for id generator [my_table_identifier.table_name] suggested; using default [persons]
Aug 23, 2022 4:02:15 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)
Aug 23, 2022 4:02:15 PM org.hibernate.metamodel.internal.EntityInstantiatorPojoStandard resolveConstructor
INFO: HHH000182: No default (no-argument) constructor for class: com.sample.app.entity.Person (class must be instantiated by Interceptor)
Hibernate:
drop table if exists employees cascade
Aug 23, 2022 4:02:15 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@35a0e495] 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.
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: table "employees" does not exist, skipping
Hibernate:
drop table if exists my_table_identifier cascade
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: table "my_table_identifier" does not exist, skipping
Hibernate:
drop table if exists persons cascade
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: SQL Warning Code: 0, SQLState: 00000
Aug 23, 2022 4:02:15 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler logWarning
WARN: table "persons" does not exist, skipping
Hibernate:
create table employees (
id integer not null,
name varchar(255),
primary key (id)
)
Aug 23, 2022 4:02:15 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@3206174f] 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 my_table_identifier (
table_name varchar(255) not null,
record_id bigint,
primary key (table_name)
)
Hibernate:
insert into my_table_identifier(table_name, record_id) values ('employees',0)
Hibernate:
insert into my_table_identifier(table_name, record_id) values ('persons',0)
Hibernate:
create table persons (
id integer not null,
name varchar(255),
primary key (id)
)
Aug 23, 2022 4:02:15 PM org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
Hibernate:
select
tbl.record_id
from
my_table_identifier tbl
where
tbl.table_name=? for update of tbl
Hibernate:
update
my_table_identifier
set
record_id=?
where
record_id=?
and table_name=?
Hibernate:
select
tbl.record_id
from
my_table_identifier tbl
where
tbl.table_name=? for update of tbl
Hibernate:
update
my_table_identifier
set
record_id=?
where
record_id=?
and table_name=?
Hibernate:
select
tbl.record_id
from
my_table_identifier tbl
where
tbl.table_name=? for update of tbl
Hibernate:
update
my_table_identifier
set
record_id=?
where
record_id=?
and table_name=?
Hibernate:
select
tbl.record_id
from
my_table_identifier tbl
where
tbl.table_name=? for update of tbl
Hibernate:
update
my_table_identifier
set
record_id=?
where
record_id=?
and table_name=?
Hibernate:
insert
into
employees
(name, id)
values
(?, ?)
Hibernate:
insert
into
employees
(name, id)
values
(?, ?)
Hibernate:
insert
into
persons
(name, id)
values
(?, ?)
Hibernate:
insert
into
persons
(name, id)
values
(?, ?)
Hibernate:
select
e1_0.id,
e1_0.name
from
employees e1_0
Employee [id=1, name=Krishna]
Employee [id=2, name=Ram]
Hibernate:
select
p1_0.id,
p1_0.name
from
persons p1_0
Person [id=1, name=Bhadri]
Person [id=2, name=Venkata]
Query the table ‘my_table_identifier’, you can confirm that a record is created for every entity that is using this table for primary key generation.
test=# \d+ my_table_identifier;
Table "public.my_table_identifier"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
------------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
table_name | character varying(255) | | not null | | extended | | |
record_id | bigint | | | | plain | | |
Indexes:
"my_table_identifier_pkey" PRIMARY KEY, btree (table_name)
Access method: heap
test=#
test=#
test=# SELECT * FROM my_table_identifier;
table_name | record_id
------------+-----------
employees | 10
persons | 10
(2 rows)
You can download this application from this link.
No comments:
Post a Comment