Hibernate @TableGenerator for Primary Key Generation in distributed systems

Paul Ravvich
Hibernate At the Gates of Mastery
3 min readMar 16, 2024

--

In the world of Java and Hibernate, managing unique identifiers for entities is a critical task, especially in distributed systems. Hibernate offers a powerful tool for this — @TableGenerator. This article will guide you step by step through setting up and using @TableGenerator, as well as explain how to create the necessary SQL queries to support this method of identifier generation.

Hi, this is Paul, and welcome to the Hibernate guide. Today we will discuss how to use @TableGenerator for organizing primary key generation in distributed systems.

What is @TableGenerator?

@TableGenerator is an annotation in Hibernate that allows customizing the generation of primary keys using a special table to store the next identifier value? This method provides flexibility and control over the identifier generation process, which is particularly useful in distributed systems.

Example

DB schema:

CREATE TABLE employee (
id BIGINT NOT NULL,
name VARCHAR(255),
PRIMARY KEY (id)
);

To start, let’s consider an example of an Employee entity that uses @TableGenerator to generate unique identifiers:

import jakarta.persistence.*;
import lombok.*;


@Entity
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "employee_gen")
@TableGenerator(
name = "employee_gen",
table = "id_gen",
pkColumnName = "gen_key",
valueColumnName = "gen_value",
pkColumnValue = "employee_id",
allocationSize = 1
)
private Long id;

@Column(name = "name")
private String name;

// equals and hashCode
}
  • name: Generator name used in annotation @GeneratedValue.
  • table: Table name used for storing identifier values.
  • pkColumnName: Column name in the table containing the key name.
  • valueColumnName: Column name containing the following key value.
  • pkColumnValue: The value in pkColumnName, which identifies the row containing the next identifier value for the given entity.
  • allocationSize: The number of values allocated at once and stored in memory. It allows reducing the number of database queries for new values.

To implement the mechanism described by @TableGenerator in Hibernate, it will be necessary to create a special table for storing the current identifier values. Here’s what the SQL for creating such a table might look like, based on the previous Employee entity example:

CREATE TABLE id_gen (
gen_key VARCHAR(255) NOT NULL,
gen_value BIGINT NOT NULL,
PRIMARY KEY (gen_key)
);

In this table, gen_key will store a unique name (or key) for each entity or group of entities for which unique identifiers need to be generated. gen_value will store the next identifier value that will be allocated.

For example, for the Employee entity we discussed, a row in this table might look like this:

INSERT INTO id_gen (gen_key, gen_value) VALUES ('employee_id', 1);

Here, employee_id is the value specified in the pkColumnValue of the @TableGenerator annotation, and it indicates that the next identifier for the Employee entity will start from 1. When Hibernate needs a new identifier, it refers to this table, increments the gen_value by allocationSize (in our example by 1, since allocationSize=1) and uses this new value as the identifier for the new entity.

If the application needs to create a new Employee record, Hibernate will first update the record in the id_gen table by incrementing gen_value:

UPDATE id_gen SET gen_value = gen_value + 1 WHERE gen_key = 'employee_id';

Then it uses this new value of gen_value as the identifier for a new record in the employee table.

This mechanism allows for avoiding identifier conflicts, especially in distributed systems, and provides more control over the process of generating unique identifiers at the cost of a slight decrease in performance due to the need for additional update and fetch operations from the id_gen table.

Advantages and disadvantages

Using @TableGenerator provides significant flexibility in managing entity identifiers, but it’s important to consider the additional database load due to the need to read and update the special identifier table. In some cases, this can lead to reduced performance, especially in situations with high competition for access to this table.

Thank you for reading until the end. Before you go:

--

--

Paul Ravvich
Hibernate At the Gates of Mastery

Software Engineer with over 10 years of XP. Join me for tips on Programming, System Design, and productivity in tech! New articles every Tuesday and Thursday!