Hibernate default entity sequence

Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?

Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.

So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!

Introduction

In this article, we are going to see how the default entity sequence changes when migrating from Hibernate 5 to Hibernate 6.

Domain Model

Let’s assume we have a Post parent entity that has a one-to-many bidirectional association with the PostComment child entity.

The Post parent entity looks like this:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private String title;

    @Column(name = "created_on")
    private LocalDate createdOn;

    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
    private List<PostComment> comments = new ArrayList<>();

    public Post addComment(PostComment comment) {
        comment.setPost(this);
        comments.add(comment);
        return this;
    }
}

And the PostComment looks as follows:

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;

    private String review;
}

Notice that both entities use the GenerationType.SEQUENCE automatic identifier generation strategy.

Hibernate 5 default entity sequence

On Hibernate 5, if you use the hbm2ddl tool to generate the database schema from the JPA entity mappings, the following database sequence will be created:

CREATE SEQUENCE hibernate_sequence START 1 INCREMENT 1

This is the default database sequence that Hibernate will use when you don’t specify the sequence name explicitly using the @SequenceGenerator annotation.

So, if we create the following Post and PostComment entities:

entityManager.persist(
    new Post()
        .setTitle(
            "High-Performance Java Persistence " +
            "eBook has been released!")
        .setCreatedOn(LocalDate.of(2016, 8, 30))
        .addComment(
            new PostComment()
                .setReview("Best book on JPA and Hibernate!")
        )
        .addComment(
            new PostComment()
                .setReview("A must-read for every Java developer!")
        )
);

entityManager.persist(
    new Post()
        .setTitle(
            "Hypersistence Optimizer has been released!")
        .setCreatedOn(LocalDate.of(2019, 3, 19))
        .addComment(
            new PostComment()
                .setReview(
                    "Hypersistence Optimizer feels like " +
                    "an additional team member"
                )
        )
);

Hibernate 5 will execute the following SQL statements:

SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')

INSERT INTO post (
    created_on, 
    title, 
    id
) 
VALUES (
    '2016-08-30', 
    'High-Performance Java Persistence eBook has been released!', 
    1
)

INSERT INTO post_comment (
    post_id, 
    review, 
    id
) 
VALUES (
    1, 
    'Best book on JPA and Hibernate!', 
    2
)

INSERT INTO post_comment (
    post_id, 
    review, 
    id
) 
VALUES (
    1, 
    'A must-read for every Java developer!', 
    3
)

INSERT INTO post (
    created_on, 
    title, 
    id
) 
VALUES (
    '2019-03-19', 
    'Hypersistence Optimizer has been released!', 
    4
)

INSERT INTO post_comment (
    post_id, 
    review, 
    id
) 
VALUES (
    4, 
    'Hypersistence Optimizer feels like an additional team member', 
    5
)

You can see that the default hibernate_sequence is used to generate the Primary Key values for both the post and post_comment table records.

Hibernate 6 default entity sequence

When using Hibernate 6, the hbm2ddl tool will the following database sequence for our Post and PostComment entities:

CREATE SEQUENCE post_SEQ START WITH 1 INCREMENT BY 50
CREATE SEQUENCE post_comment_SEQ START WITH 1 INCREMENT BY 50

So, comparing to Hibernate 5, there are two major differences in Hibernate 5:

First, each entity gets its own database sequence instead of using a global sequence for all entities.

Second, the allocation size is 50, not 1. This is because the default allocationSize of the JPA @SequenceGenerator is 50:

@Repeatable(SequenceGenerators.class)
@Target({TYPE, METHOD, FIELD}) 
@Retention(RUNTIME)
public @interface SequenceGenerator {

    String name();

    String sequenceName() default "";

    String catalog() default "";

    String schema() default "";

    int initialValue() default 1;

    int allocationSize() default 50;
}

So, when persisting the same Post and PostComment entities as we did previously in our Hibernate 5 section, then Hibernate 6 will execute the following SQL statements:

SELECT nextval('post_SEQ')
SELECT nextval('post_comment_SEQ')
SELECT nextval('post_comment_SEQ')
SELECT nextval('post_SEQ')

INSERT INTO post (
    created_on, 
    title, 
    id
) 
VALUES (
    '2016-08-30', 
    'High-Performance Java Persistence eBook has been released!', 
    1
)

INSERT INTO post_comment (
    post_id, 
    review, 
    id
) 
VALUES (
    1, 
    'Best book on JPA and Hibernate!', 
    1
)

INSERT INTO post_comment (
    post_id, 
    review, 
    id
) 
VALUES (
    1, 
    'A must-read for every Java developer!', 
    2
)

INSERT INTO post (
    created_on, 
    title, 
    id
) 
VALUES (
    '2019-03-19', 
    'Hypersistence Optimizer has been released!', 
    2
)

INSERT INTO post_comment (
    post_id, 
    review, 
    id
) 
VALUES (
    2, 
    'Hypersistence Optimizer feels like an additional team member', 
    3
)

By default, Hibernate uses the pooled optimizer when the sequence allocationSize is greater than 1.

The reason why there are two sequence calls for both entity sequences is that the pooled optimizer needs to get the first identifier value that’s greater than 1, and for that, it has to call the sequence twice because the first call returns the initial value of 1.

However, even if we created 3 post_comment records, the post_comment_SEQ sequence was not called 3 times since the default allocation size is 50.

We can see this in practice when creating 5 more Postentities in a successive transaction:

for (int i = 1; i <= 5; i++) {
    entityManager.persist(
        new Post()
            .setTitle(String.format("Post number %d", i))
            .setCreatedOn(LocalDate.now())
    );
}

As Hibernate 6 will generate the following statements:

INSERT INTO post (created_on, title, id) VALUES ('2023-05-09', 'Post number 1', 3)
INSERT INTO post (created_on, title, id) VALUES ('2023-05-09', 'Post number 2', 4)
INSERT INTO post (created_on, title, id) VALUES ('2023-05-09', 'Post number 3', 5)
INSERT INTO post (created_on, title, id) VALUES ('2023-05-09', 'Post number 4', 6)
INSERT INTO post (created_on, title, id) VALUES ('2023-05-09', 'Post number 5', 7)

Until we generate 50 Post entities from multiple transactions, the post_SEQ won’t be called again.

Migrating to Hibernate 6

When migrating to Hibernate 6, if you relied on the default Hibernate 5 sequence behavior, then you might have some issues since now Hibernate expects to find entity-specific sequences that you might be missing.

So, there are two ways to do this default sequence migration.

The best way is to use entity-specific sequences since they allow you to reduce the number of calls to the database sequence.

If you don’t want to do that and you want to share the same database hibernate_sequence for all your entities, then you must change your entity identifier mapping to use it explicitly, like this:

@Id
@GeneratedValue(
    strategy = GenerationType.SEQUENCE, 
    generator = "hibernate_sequence"
)
@SequenceGenerator(
    name = "hibernate_sequence", 
    allocationSize = 1
)
private Long id;

That’s it!

I'm running an online workshop on the 20-21 and 23-24 of November about High-Performance Java Persistence.

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Conclusion

When migrating to Spring Boot 3 or Spring 6, you will automatically migrate to Hibernate 6 as well, and knowing what’s changed in Hibernate can be very useful.

If you’re interested in more tips about this migration, then check out this article as well.

Transactions and Concurrency Control eBook

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.