Check @Id column value against database before persisting
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If an entity uses a manually assigned @Id, it is technically possible to check whether that value already exists before calling persist, but that check is not enough by itself. The real guarantee must still come from a database primary-key or unique constraint, because a pre-check can race with another transaction.
The Database Must Remain the Source of Truth
A common instinct is to query first and insert second. That can improve user feedback, but it does not guarantee uniqueness under concurrency. Two requests can both observe “not found” and then both try to insert the same ID.
That is why the safest design is usually one of these:
- let the database generate the identifier
- use a globally unique identifier strategy such as UUID
- keep the database constraint and handle duplicate-key failure cleanly
The existence check is optional convenience logic, not the final protection.
Check Existence with find or a Query
If you still want an explicit pre-check, use EntityManager.find when you know the entity class and ID.
That is simpler and usually clearer than building a manual count query for the same key.
You can also use a repository method in Spring Data JPA.
That reads well in service code, but it still does not remove the concurrency race.
Keep the Constraint and Catch the Failure
Even with a pre-check, your insert path should still expect the database to reject duplicate IDs.
The explicit flush forces the insert attempt during the current transaction scope, which makes it easier to map the failure to a user-facing response instead of discovering it much later.
Prefer Generated IDs When You Can
If the application does not have a strong business reason to assign the ID manually, generated identifiers are usually the better design.
Generated IDs remove a whole class of duplicate-ID bugs and simplify persistence code.
Use Business Validation on a Separate Unique Field
Sometimes the real requirement is not “check the primary key,” but “check that a business key is not already used.” In that case, the uniqueness rule often belongs on a separate column with a unique constraint.
For example, if the real uniqueness concern is an external code, model that explicitly instead of overloading the primary key check.
That design expresses intent better than forcing a natural business identifier into the persistence ID slot.
Think About Transaction Boundaries
If you do a pre-check, do it in the same transactional context as the insert path so the behavior is consistent. Even then, only the database constraint truly resolves concurrency. The application layer can improve user messaging, but the database remains the authority on uniqueness.
Common Pitfalls
- Treating a pre-insert existence check as if it guarantees uniqueness under concurrent writes.
- Writing a manual count query when
findorexistsByIdwould be simpler. - Using manual IDs when generated IDs would remove the whole problem.
- Forgetting to flush and therefore discovering duplicate-key errors later than expected.
- Using the primary key to represent a business-unique field that should really have its own unique constraint.
Summary
- You can check an
@Idvalue before persisting, but that check is not enough by itself. - The database constraint is the real protection against duplicate IDs.
- Use
findorexistsByIdfor simple pre-checks when user feedback matters. - Prefer generated IDs if manual assignment is not required.
- Model business uniqueness explicitly and handle persistence failures cleanly.

