If you are manually assigning IDs to entities instead of using @GeneratedValue , you may inadvertently try to reuse an ID that is already present in the table.
Wrap the save logic in a try-catch block specifically for DataIntegrityViolationException . This allows the application to return a user-friendly error message (e.g., "Username already taken") instead of a generic 500 Internal Server Error. If you are manually assigning IDs to entities
In a multi-threaded environment, two processes might check if a value (like an email address) exists at the same time. Both see that it doesn’t, both attempt to insert it, and the second one fails. In a multi-threaded environment, two processes might check
Use a repository method like existsByEmail(String email) before attempting a save. While this doesn't solve high-concurrency race conditions, it eliminates the majority of "honest" mistakes. In a multi-threaded environment