01. A Useful Story About GeneratedValue Part 1


The beginning of today's post
Getting Started
When creating an Entity, we designate a PK (Primary Key).
Among them, when choosing a Primary Key strategically, we cannot give up on sequential increment values.
So let's explore how JPA generates these surrogate keys and how they work.
What is GeneratedValue?
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
@Table(name = "coupon")
@Entity
@Getter
@Setter
public class Coupon implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "coupon_id", insertable = false, nullable = false)
private Long couponId;
@Column(name = "coupon", nullable = false, unique = true)
private String coupon;
@Column(name = "coupon_status", nullable = false)
private CouponStatus status;
@Column(name = "reg_timestamp", nullable = false)
private LocalDateTime regTimestamp;
@Column(name = "expired_timestamp", nullable = false)
private LocalDateTime expiredTimestamp;
}
JPA provides automatic primary key generation through the '@Id' and '@GeneratedValue' annotations.
There are 4 auto-generation strategies available:
Mode Description
Auto (Default) JPA decides automatically
IDENTITY Delegates primary key generation to the DB
SEQUENCE
Uses SEQUENCE if supported by DB, otherwise Table Mode
TABLE Creates a key table in DB (hibernate_sequence)
How GeneratedType.AUTO Works

When using AUTO (Default), the system operates according to the flowchart above.
-
If it's a UUID, process it as UUID.
-
If it's not a UUID and is a Number Data Type, check
[hibernate.id.new_generator_mapping](https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#_identifier_options).
2-1. If FALSE, use Native Generator (e.g., **auto_increment** for MySQL)
- If hibernate.id.new_generator_mapping is TRUE, use
SequenceStyleGenerator.
3-1. Make a Sequence Call and receive the ID.
3-2. If the DBMS doesn't support the Sequence API, it falls back to GeneratedType.TABLE.
How GeneratedType.IDENTITY Works
This method delegates all ID generation to the database.
During the actual insertion phase, it's set to NULL and passed to the DB.
Ultimately, JPA doesn't fill in the ID itself.
However, to maintain the integrity of the persistence context, after inserting data, it retrieves which ID was used for storage.
BULK INSERT with GeneratedType.IDENTITY?

Recently, I had a case where I needed to do BULK INSERT with JPA (Hibernate implementation).
The performance was terrible, so I printed out the SQL queries and found they were being sent as-is.
I found the reason in the Hibernate official documentation.
[
Hibernate ORM 5.4.17.Final User Guide Fetching, essentially, is the process of grabbing data from the database and making it available to the application. Tuning how an application does fetching is one of the biggest factors in determining how an application will perform. Fetching too much dat docs.jboss.org

This is probably one of the most common memes on blogs..
Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator.
This means that when inserting data with 'IDENTITY', batch insert is disabled at the JDBC level.
Since the official documentation didn't explain why, I found a StackOverflow post from another blog.
[
Hibernate disabled insert batching when using an identity identifier generator The Hibernate documentation says: Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator. But all my entities have this configuration:... stackoverflow.com
The only drawback is that we can't know the newly assigned value prior to executing the INSERT statement. This restriction is hindering the "transactional write behind" flushing strategy adopted by Hibernate. For this reason, Hibernates disables the JDBC batch support for entities using the IDENTITY generator.
One-line summary: It can cause issues with Hibernate's 'transactional write behind', so no matter how much you request it, I'll turn it off...
Batch Insert with GeneratedType.IDENTITY?

??????????????????????
If I just say "use SEQUENCE everyone(?)", that would be an irresponsible answer. If your backend database is MySQL, please read the article below.
[
HomoEfficio/dev-tips A collection of small problems and solutions encountered during development. Contribute to HomoEfficio/dev-tips development by creating an account on GitHub. github.com

One-line summary image
MySQL doesn't have a SEQUENCE API... Therefore, Hibernate tries to use a Table format, which leads to the disaster shown above. (Words fail me...)
Anyway, getting back to the main point, let's revisit that StackOverflow post.
Therefore, using IDENTITY is still the best choice on MySQL, and if you need batching for insert, you can use jOOQ for that. Hibernate and jOOQ are a great combo.
The conclusion is that it's difficult to handle with Hibernate alone, so use jOOQ together. They say the two are a great combination.
In the end, I also implemented it with jOOQ, and there were quite a few frustrating issues.
I'll cover this in another series.
What's in Part 2?
Originally, I tried to cover everything in one post, but it turned out to be longer than expected.
And since there's quite a long story about GeneratedType.SEQUENCE, I decided to split it up.
In Part 2, I'll discuss 'GeneratedType.SEQUENCE', 'GeneratedType.TABLE', and the conclusion.