Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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

StrategyWho creates the valueColumn
AUTO (default)Hibernate picks; for H2, PostgreSQL and Oracle it is a sequencebigint
SEQUENCEA database sequencebigint
IDENTITYThe database, as an auto-increment columnbigint generated by default as identity
UUIDHibernate, a random UUIDuuid

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 insert immediately on persist. Because of this it cannot group inserts into batches.
  • SEQUENCE: Hibernate takes values from the sequence ahead of time. With allocationSize = 10 one call to the database reserves ten ids, so the third comment got its id without any query. The insert statements wait until commit. The default allocationSize is 50 — that is the increment by 50 you 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.Post

Either let Hibernate generate the id, or remove @GeneratedValue and always assign it yourself.

Thanks for reading!

Donate to the site — we work hard for you.