The best way to generate a TSID entity identifier with JPA and Hibernate

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, I will show you the best way to generate a TSID entity identifier with JPA and Hibernate.

Prior to continuing, if you are not familiar with the advantages of using compact Time-Sorted Identifiers, like TSID, check out this article first.

Hypersistence Utils

As promised, the Hypersistence Utils OSS library keeps on evolving, adding more and more utilities to help you implement a high-performance data access layer.

Starting with the 3.2 version, Hypersistence Utils provides a TSID entity identifier generator that can automatically assign time-sorted identifiers when you persist a given entity.

Depending on the Hibernate version you are using, there are two ways you can use this TSID entity identifier generator.

Generate a TSID entity identifier with JPA and Hibernate 6

If you’re using Hibernate 6, then you first need to add the hypersistence-utils-hibernate-60 dependency to your Maven pom.xml file:

<dependency>
    <groupId>io.hypersistence</groupId>
    <artifactId>hypersistence-utils-hibernate-60</artifactId>
    <version>${hypersistence-utils.version}</version>
</dependency>

Afterward, you can use the @Tsid annotation right next to the @Id annotation, as illustrated by the following entity mapping:

@Entity(name = "Post")
@Table(name = "post")
public class Post {
⠀
    @Id @Tsid
    private Long id;
⠀
    private String title;
⠀
    public Long getId() {
        return id;
    }
⠀
    public Post setId(Long id) {
        this.id = id;
        return this;
    }
⠀
    public String getTitle() {
        return title;
    }
⠀
    public Post setTitle(String title) {
        this.title = title;
        return this;
    }
}

And, when we persist the Post entity:

entityManager.persist(
    new Post()
        .setTitle("High-Performance Java Persistence")
);

Hibernate will generate the following SQL INSERT statement:

INSERT INTO post (
    title, 
    id
) 
VALUES (
    'High-Performance Java Persistence', 
    414142131136227703
)

The @Tsid annotation works on Long, String, or TSID entity identifier types and generates the appropriate time-sorted Object type.

Generate a TSID entity identifier with JPA and Hibernate 5

If you’re using Hibernate 5, then you need to add one of the following dependencies to your project:

  • hypersistence-utils-hibernate-55 for Hibernate 5.6 and 5.5
  • hypersistence-utils-hibernate-52 for Hibernate 5.4, 5.3, and 5.2

For Hibernate 5.6, we will first add the hypersistence-utils-hibernate-55 dependency like this:

<dependency>
    <groupId>io.hypersistence</groupId>
    <artifactId>hypersistence-utils-hibernate-55</artifactId>
    <version>${hypersistence-utils.version}</version>
</dependency>

Afterward, you can use the TsidGenerator identifier generator, as illustrated by the following entity mapping:

@Entity(name = "Post")
@Table(name = "post")
public class Post {
⠀
    @Id
    @GeneratedValue(generator = "tsid")
    @GenericGenerator(
        name = "tsid",
        strategy = "io.hypersistence.utils.hibernate.id.TsidGenerator"
    )
    private Long id;
⠀
    private String title;
⠀
    public Long getId() {
        return id;
    }
⠀
    public Post setId(Long id) {
        this.id = id;
        return this;
    }
⠀
    public String getTitle() {
        return title;
    }
⠀
    public Post setTitle(String title) {
        this.title = title;
        return this;
    }
}

And, when we persist the Post entity:

entityManager.persist(
    new Post()
        .setTitle("High-Performance Java Persistence")
);

Hibernate will generate the following SQL INSERT statement:

INSERT INTO post (
    title, 
    id
) 
VALUES (
    'High-Performance Java Persistence', 
    414147868608769831
)

The TsidGenerator generator can assign time-sorted values for Long, String, or TSID entity identifier types.

How to customize the TSID value generator

If you want to customize the TSID generation, then you can provide a class that extends the Supplier<TSID.Factory>, like this one:

public static class CustomTsidSupplier implements Supplier<TSID.Factory> {
⠀
    @Override
    public TSID.Factory get() {
        return TSID.Factory.builder()
            .withNodeBits(1)
            .build();
    }
}

For Hibernate 6, the CustomTsidSupplier can be provided via the @Tsid annotation:

@Id
@Tsid(CustomTsidSupplier.class)
private Long id;

And for Hibernate 5, like this:

@Id
@GeneratedValue(generator = "tsid")
@GenericGenerator(
    name = "tsid",
    strategy = "io.hypersistence.utils.hibernate.id.TsidGenerator",
    parameters = @Parameter(
        name = TsidGenerator.TSID_FACTORY_SUPPLIER_PARAM,
        value = "io.hypersistence.utils.hibernate.id.CustomTsidSupplier"
    )
)
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

While you can also assign the TSID identifier value manually prior to persisting the JPA or Hibernate entity, using the Hypersistence Utils @Tsid or the TsidGenerator is even more convenient.

If you want to give it a try, check out the TsidIdentifierTest integration test in the Hypersistence Utils project.

Transactions and Concurrency Control eBook

2 Comments on “The best way to generate a TSID entity identifier with JPA and Hibernate

  1. Hi Vlad.

    Thanks for your great articles. I’ve learned a lot with them. I’ve got a requirement where a single Spring-Hibernate stack is handling several MySQL catalogs, where each one is for a different tenant. However some tenants may be consolidated in a single catalog later, so I want to avoid possible key clashes. My solution is that the TSID generation could have a different node id for each tenant.

    Please, could you give me any advice for this approach?.

    Best regards,

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.