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

Are you struggling with performance issues in your Spring, Jakarta EE, or Java EE application?

What if there were a tool that could automatically detect what caused performance issues in your JPA and Hibernate data access layer?

Wouldn’t it be awesome to have such a tool to watch your application and prevent performance issues during development, long before they affect production systems?

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

So, rather than fixing performance issues in your production system on a Saturday night, you are better off using Hypersistence Optimizer to help you prevent those issues so that you can spend your time on the things that you love!

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.5, 6.4, or 6.3 then you first need to add the hypersistence-utils-hibernate-63 dependency to your Maven pom.xml file:

<dependency>
    <groupId>io.hypersistence</groupId>
    <artifactId>hypersistence-utils-hibernate-63</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

For Hibernate 5.6 and 5.5, 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!

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

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

  1. I’ve been meaning to try this out for ages. Finally got around to it. Really easy change to make with Hibernate 6 – thanks for this. We had the index issue with a real project a while ago, I hope I’ll use TSID in the future.

  2. 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.