eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Introduction

In this tutorial, we’re going to look at Time-Sorted Unique Identifiers (TSIDs). In particular, we’re going to look at the Hypersistence TSID library to work with these IDs in Java.

2. What are TSIDs

TSIDs, or Time-Sorted Unique Identifiers, are identifiers we can guarantee are unique and can be naturally sorted by the time they were generated. Additionally, we can retrieve values from these IDs to determine the exact time each ID was generated.

The Hypersistence TSID library then allows us to represent these generated IDs as either a 64-bit integer or a Base-32 encoded 13-character string. This allows us to store or transfer the ID in formats optimized for either machine processing or human readability.

2.1. TSID Structure

Hypersistence TSIDs store values as 64-bit integers. This consists of three portions:

  • Milliseconds since the epoch. This is always 42 bits in size.
  • Node ID. We can configure this to be between 0 and 20 bits in size.
  • Counter. This is between 2 and 22 bits in size, depending on the size of the Node ID.

By default, the node ID is 10 bits in length. This then means the counter is 12 bits long.

The counter portion exists to ensure unique values are generated even within the same millisecond. The default size of 12 bits means we can generate 2^12 = 4,096 unique values per millisecond. Increasing this up to 22 bits would instead allow us to generate up to 4,194,304 unique values per millisecond.

The node ID portion then acts as an identifier of the exact node that generated the ID. This allows us to generate unique IDs across different servers without any complex coordination. We can simply assign each server a different node ID, and this then guarantees values generated on different nodes will never be able to collide.

For example, we might have an ID value of 38,352,658,567,418,867. This breaks down as:

00000000 10001000 01000001 10001010 00101110 00011010 01010011 11110011
|-----------||---------||---------------------------------------------|
|  Counter  || Node ID ||                  Timestamp                  |
  • Timestamp = 1,692,990,591,987. This represents Friday, 25 August 2023 19:09:51.987 UTC.
  • Node ID = 528
  • Counter = 8

The next ID generated in the same millisecond by this node will have a counter value of 9. And an ID generated by a different node will have a different node ID value. As such, all of those IDs would be safely unique.

3. Dependencies

Before using Hypersistence TSID, we need to include the latest version in our build, which is 2.1.4 at the time of writing.

If we’re using Maven, we can include this dependency in our pom.xml file:

<dependency>
    <groupId>io.hypersistence</groupId>
    <artifactId>hypersistence-tsid</artifactId>
    <version>2.1.4</version>
</dependency>

At this point, we’re ready to start using it in our application.

4. Generating TSIDs

Once we have Hypersistence TSID available, we’re ready to use it to generate our IDs.

Normally, we’ll create TSIDs using a factory. We can get started quickly by using a provided singleton instance:

TSID.Factory tsidFactory = TSID.Factory.INSTANCE;

Alternatively, we can create our own instance:

TSID.Factory tsidFactory = TSID.Factory.builder().build();

We’ll see later how we can customise this to our needs.

Our TSID Factory is threadsafe. As such, we should create one instance per node and reuse it everywhere we need to generate new IDs to ensure that they’re always unique.

Once we have a TSID Factory, we can use it to generate TSIDs using generate():

TSID tsid = tsidFactory.generate();

This will always return the next unique TSID, respecting the current system time, the current counter value and the configured node value.

Our TSID instances correctly implement equals() and hashCode(), and are also Comparable, so we can safely use them in whatever context we need.

If we’re using the singleton factory, we also have a shorthand that we can use:

TSID tsid = TSID.Factory.getTsid();

This is exactly the same as above, though slightly shorter to write.

Alternatively, we have a simple option to very efficiently get a TSID:

TSID tsid = TSID.fast();

This prioritizes speed, so it cuts some corners during generation. In particular, it ignores the settings for the node from which we generate the ID, and the counter simply increments indefinitely without resetting when the time changes. However, if performance is more important than accuracy, then this is a good option.

4.1. TSID Factory

We’ve seen how to create a TSID Factory used to generate new TSIDs. When we do this, we have several ways to configure it to better suit our needs.

Most usefully, we can specify the value for the node section of the generated ID. We can specify this in a number of ways:

  • The withNode() method on our factory builder.
  • The system property tsid.node.
  • The environment variable TSID_NODE.
  • Using a random number.

This lets us fully control which value to use while still ensuring that a value is always provided.

We can set the number of bits used for the node in a similar manner:

  • The withNodeBits() method on our factory builder.
  • The system property tsid.node.count.
  • The environment variable TSID_NODE_COUNT.
  • The default value “10”.

By setting these values through system properties and environment variables, we can easily vary them across different instances of our application, helping ensure that the generated IDs remain unique.

In addition to specifying the node, we can also customize the generation of the timestamp portion. We’re able to provide an additional clock instead of using the system clock, and even specify the value to use for the epoch instead of the default:

TSID.Factory factory = TSID.Factory.builder()
    .withClock(clock)
    .withCustomEpoch(Instant.parse("2000-01-01T00:00:00Z"))
    .build();

Here we provide a custom clock and set the epoch to be January 1st 2000, instead of 1970.

5. Serializing TSIDs

Once we’ve generated a TSID instance, we need to be able to serialize, and potentally deserialize, them.

The TSID class has support for converting the generated ID to either a Long – structured as described above – or to a String:

long tsidLong = tsid.toLong(); // 809100737063473402
String tsidString = tsid.toString(); // 0PEM0TNJ2SM7T

Both represent the same ID, only in different formats. We can use these as appropriate. For example, the long integer format is more memory-efficient and easier to work with, but may have issues in certain languages (e.g. JavaScript uses a floating-point format for numbers, so it may have accuracy issues with numbers of this size).

We can also convert these string and long formats back into TSID instances using TSID.from():

TSID tsid = TSID.from(tsidLong);
TSID tsid = TSID.from(tsidString);

These produce exactly the same value that the system originally generated. This means we can store or transfer our IDs however we wish and restore them to their original state.

We can also extract the timestamp portion of the TSID if needed. This can be either as an Instant or as milliseconds since the epoch:

Instant instant = tsid.getInstant();
long millis = tsid.getUnixMilliseconds();

We can’t access the node or counter values. We’d need to know how many bits are assigned to each to get this, and the TSID doesn’t store that information.

6. Summary

In this article, we looked at the concept of Time-Sorted Unique Identifiers and used the Hypersistence TSID library to work with them in Java. Next time you need to generate unique IDs, why not give it a try?

As usual, all of the examples from this article are available over on GitHub.

Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)
guest
0 Comments
Oldest
Newest
Inline Feedbacks
View all comments