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 article, we’ll discuss what ObjectId is, how we can generate it, and possible ways of ensuring its uniqueness.

2. ObjectId General Information

Let’s start by explaining what an ObjectId is. An ObjectId is a 12-byte hexadecimal value and one of the possible datatypes in BSON specification. BSON is a binary serialization of a JSON document. Moreover, MongoDB uses ObjectId as its default identifier for the _id field in documents. There is also a default unique index on the _id field set up when a collection is created.

This prevents users from inserting two documents having the same _id. Moreover, the _id index can not be dropped from the collection. However, it’s possible to have a single document with the same _id inserted into two collections.

2.1. ObjectId Structure

ObjectId can be divided into three different parts. Considering ObjectId of 6359388c80616b1fc6d7ec71, the first part would consist of 4 bytes – 6359388c. Those 4 bytes represent time in seconds since the Unix Epoch. The second part consists of the next 5 bytes, which are 80616b1fc6. Those bytes represent a random value generated once per process. The random value is unique to the machine and process. The last part is 3 bytes d7ec71, and it represents an incrementing counter which starts from a random value.

It’s also worth mentioning that the above structure is valid for MongoDB in version 4.0 and above. Before that, there were four parts of which the ObjectId was constructed. The first 4 bytes represent seconds since the Unix Epoch, and the next three are for the machine identifier.

Next 2 bytes for the process id and the last 3 bytes for the counter start from a random value.

2.2. ObjectId Uniqueness

The most important thing, which is also mentioned in the MongoDB documentation, is that the ObjectId is highly likely considered to be unique when generated. That being said, there is a very slim possibility of generating a duplicate ObjectId. Looking at the structure of ObjectId, we can see that there are over 1,8×10^19 possibilities for ObjectId to be generated within one second.

Even if all ids were generated within the same second on the same machine within the same process, that would be over 17 million possibilities just for the counter itself.

3. ObjectId Creation

There are multiple ways of creating ObjectId in Java. It can be done either with non-parameters or parametrized constructors.

3.1. ObjectId Creation With Non-parameterized Constructors

The first and one of the easiest ones is via a new keyword with the non-parametrized constructor:

ObjectId objectId = new ObjectId();

The second is simply calling a static method get() on an ObjectId class. Not directly calling the non-parametrized constructors. However, the implementation of the get() method consists of creating ObjectId the same as in the first example – through the new keyword:

ObjectId objectId = ObjectId.get();

3.2. ObjectId Creation With Parameterized Constructors

The rest of the examples use parametrized constructors. We can create an ObjectId by passing the Date class as a parameter or both the Date class and int counter. If we try to create ObjectId with the same Date in both methods, we’ll get a different ObjectId for new ObjectId(date) vs. new ObjectId(date, counter).

However, if we create two ObjectId through new ObjectId(date, counter) in the same second, we’ll get a duplicate ObjectId since it was generated in the same second, on the same machine, and with the same counter. Let’s see an example:

@Test
public void givenSameDateAndCounter_whenComparingObjectIds_thenTheyAreNotEqual() {
    Date date = new Date();
    ObjectId objectIdDate = new ObjectId(date); // 635981f6e40f61599e839ddb
    ObjectId objectIdDateCounter1 = new ObjectId(date, 100); // 635981f6e40f61599e000064
    ObjectId objectIdDateCounter2 = new ObjectId(date, 100); // 635981f6e40f61599e000064

    assertThat(objectIdDate).isNotEqualTo(objectIdDateCounter1);
    assertThat(objectIdDate).isNotEqualTo(objectIdDateCounter2);

    assertThat(objectIdDateCounter1).isEqualTo(objectIdDateCounter2);
}

Additionally, it’s possible to create ObjectId by providing a hexadecimal value straight as a parameter:

ObjectId objectIdHex = new ObjectId("635981f6e40f61599e000064");

There’re a few more possibilities to create an ObjectId. We can pass byte[] or ByteBuffer class. If we create an ObjectId by passing an array of bytes to a constructor, we should get the same ObjectId by creating it through ByteBuffer class using the same array of bytes.

Let’s see an example:

@Test
public void givenSameArrayOfBytes_whenComparingObjectIdsCreatedViaDifferentMethods_thenTheObjectIdsAreEqual(){
    byte[] bytes = "123456789012".getBytes();
    ObjectId objectIdBytes = new ObjectId(bytes);

    ByteBuffer buffer = ByteBuffer.wrap(bytes);
    ObjectId objectIdByteBuffer = new ObjectId(buffer);

    assertThat(objectIdBytes).isEqualTo(objectIdByteBuffer);
}

The last possible method would be to create an ObjectId by passing a timestamp and a counter to a constructor.

4. Pros and Cons of ObjectId

As with all things, there are pros and cons worth knowing about.

4.1. Benefits of ObjectId

Since ObjectId is 12-byte long, it’s smaller than the 16-byte UUID. That being said, if we have a lot of documents in the database using ObjectId rather than UUID, we’ll save some space. Around 26500 usages of ObjectId will save about 1MB compared to UUID. This seems to be a minimal amount.

Still, if the database is large enough and it’s also possible that a single document will have more than one occurrence of the ObjectId, then the gain of disk space and RAM might be significant since the documents, in the end, will be smaller. Secondly, as we learned before, a timestamp is embedded into the ObjectId, which might be useful in some cases.

For instance, to determine which ObjectId was created first, assuming all of them were autogenerated and not created by manipulating the Date class into the parametrized constructor as we’ve seen before.

4.2. Drawbacks of ObjectId

On the other hand, there are some identifiers even smaller than a 12-byte ObjectId, which again would save even more disk space and RAM. Furthermore, since ObjectId is just a generated hexadecimal value, this means there is a possibility of having a duplicate id. It’s very slim, but it’s still possible.

5. Ensuring the Uniqueness of ObjectId

If we have to ensure that the generated ObjectId is unique, we can try to program a bit around it to make it 100% sure it’s not a duplicate.

5.1. Try Catch DuplicateKeyException

Suppose we insert a document with a field _id already in the database. In that case, we can catch a DuplicateKeyException and retry the inserting operation until it’s successful. This method will only work on fields that have a unique index created.

Let’s see an example of that. Considering a User class:

public class User {
    public static final String NAME_FIELD = "name";

    private final ObjectId id;
    private final String name;

    // constructor
    // getters
}

We’ll insert a User into the database and then try to insert another one with the same ObjectId. This will cause DuplicateKeyException to be thrown. We can catch that and retry the insert operation of User. However, this time, we’ll generate another ObjectId. For the purpose of this test, we’ll use an embedded MongoDB library and Spring Data with MongoDB.

Let’s see an example:

@Test
public void givenUserInDatabase_whenInsertingAnotherUserWithTheSameObjectId_DKEThrownAndInsertRetried() {
    // given
    String userName = "Kevin";
    User firstUser = new User(ObjectId.get(), userName);
    User secondUser = new User(ObjectId.get(), userName);

    mongoTemplate.insert(firstUser);

    // when
    try {
        mongoTemplate.insert(firstUser);
    } catch (DuplicateKeyException dke) {
        mongoTemplate.insert(secondUser);
    }

    // then
    Query query = new Query();
    query.addCriteria(Criteria.where(User.NAME_FIELD)
      .is(userName));
    List<User> users = mongoTemplate.find(query, User.class);
    assertThat(users).usingRecursiveComparison()
      .isEqualTo(Lists.newArrayList(firstUser, secondUser));
}

5.2. Find and Insert

Another approach, probably not recommended, could be to find a document with a given ObjectId to see if it exists. If it doesn’t exist, we could insert it. Otherwise, throw an error or generate another ObjectId and try again. This method is also unreliable since there is no atomic find and insert option in MongoDB, which could lead to inconsistencies.

It’s a common approach to autogenerate ObjectId and try to insert a document without ensuring its uniqueness. It seems to be overkill to on each insert try catch DuplicateKeyException and retry the operation. The number of edge cases is very limited, and it’s tough to reproduce such a case without seeding ObjectId with either Date, counter or timestamp in the first place.

However, if, for some reason, we can’t afford to have a duplicate ObjectId due to those edge cases, then we’d consider using the above method to ensure global uniqueness.

5. Conclusion

In this article, we learned what an ObjectId is, how it’s built, how we can generate it, and possible ways of ensuring its uniqueness. In the end, it seems to be the best idea to trust the autogeneration of ObjectIds.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
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)