Identifiers
Every entity needs a primary key — an identifier that is unique within the table. Usually the program does not invent it: the database or Hibernate generates it. How exactly is chosen with the strategy parameter of @GeneratedValue, and the choice affects when the insert is executed.
Strategies
| Strategy | Who creates the value | Column |
|---|---|---|
AUTO (default) | Hibernate picks; for H2, PostgreSQL and Oracle it is a sequence | bigint |
SEQUENCE | A database sequence | bigint |
IDENTITY | The database, as an auto-increment column | bigint generated by default as identity |
UUID | Hibernate, a random UUID | uuid |
Three entities, three strategies
// src/main/java/com/example/blog/Post.java
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
// constructors and getters
}// src/main/java/com/example/blog/Comment.java
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@SequenceGenerator(sequenceName = "comment_seq", allocationSize = 10)
private Long id;
private String text;
// constructors and getters
}// src/main/java/com/example/blog/Tag.java
@Entity
public class Tag {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String name;
// constructors and getters
}Annotations come from jakarta.persistence, UUID from java.util. The program saves one post, three comments and a tag and prints their ids right after persist:
Hibernate: create sequence comment_seq start with 1 increment by 10
Hibernate: create table Comment (id bigint not null, text varchar(255), primary key (id))
Hibernate: create table Post (id bigint generated by default as identity, title varchar(255), primary key (id))
Hibernate: create table Tag (id uuid not null, name varchar(255), primary key (id))
Hibernate: insert into Post (title,id) values (?,default)
Post id: 1
Hibernate: select next value for comment_seq
Comment id: 1
Hibernate: select next value for comment_seq
Comment id: 2
Comment id: 3
Tag id: 0807f8ef-7f04-4dfe-b207-c3e9f3ff3bfa
Hibernate: insert into Comment (text,id) values (?,?)
Hibernate: insert into Comment (text,id) values (?,?)
Hibernate: insert into Comment (text,id) values (?,?)
Hibernate: insert into Tag (name,id) values (?,?)What the output shows
- IDENTITY: the id is known only after the row is inserted, so Hibernate runs the
insertimmediately onpersist. Because of this it cannot group inserts into batches. - SEQUENCE: Hibernate takes values from the sequence ahead of time. With
allocationSize = 10one call to the database reserves ten ids, so the third comment got its id without any query. Theinsertstatements wait until commit. The defaultallocationSizeis 50 — that is theincrement by 50you saw earlier. - UUID: the value is created in Java, the database is not involved at all.
For new projects the default sequence is a good choice. UUID is useful when ids are created in several places or must not be guessable.
Natural identifiers
Besides the surrogate key, a row often has a unique business key — for example, the address part (slug) of a post. Hibernate calls it a natural id:
// src/main/java/com/example/blog/Post.java
@Id
@GeneratedValue
private Long id;
@NaturalId
private String slug;@NaturalId comes from org.hibernate.annotations and adds a unique constraint. A post can be loaded by it:
// src/main/java/com/example/blog/Main.java
Post post = emf.callInTransaction(em ->
em.find(Post.class, "hello-hibernate", KeyType.NATURAL));The query is select ... from Post p1_0 where p1_0.slug=?. KeyType is in the org.hibernate package.
Common mistake
If an entity has @GeneratedValue, do not set the id yourself. Hibernate sees a filled id and decides that the object already exists in the database:
Exception in thread "main" jakarta.persistence.EntityExistsException: Detached entity passed to persist: com.example.blog.PostEither let Hibernate generate the id, or remove @GeneratedValue and always assign it yourself.