๐ด The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
2026-02-18 11:20:15.123 ERROR 8842 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.devinhyderabad.entity.User] with root cause
org.hibernate.PersistentObjectException: detached entity passed to persist: com.devinhyderabad.entity.Userโก Quick Fix Works 80% of the time
If using `EntityManager`, change `persist()` to `merge()`. If using Spring Data JPA, ensure your ID isn't manually set before `save()`.
// EntityManager approach
entityManager.merge(detachedUser);
// Spring Data JPA approach
User user = new User();
// user.setId(1L); <-- REMOVE THIS LINE
repository.save(user);๐ง Why this Happens
Tap to expand the deep technical explanation
You passed an entity to `persist()` (or `save()` on a new entity) that already has its Primary Key (`@Id`) assigned. Hibernate's `persist()` expects a brand new, un-ID'd object. Because the object has an ID, Hibernate assumes it must already exist in the database (it's 'detached'), and refuses to insert it.
The HITEC City Parking Spot Analogy:
Imagine a factory worker trying to stamp a brand-new serial number onto a product, but the product already has a serial number engraved on it. The worker refuses, saying, 'This isn't a new product; it already has an identity.'
๐ How to Reproduce Confirm this is your error
Create a new `User` object. Manually set its ID: `user.setId(1L)`. Call `entityManager.persist(user)` or `repository.save(user)` (if the entity's ID generator is set to IDENTITY/SEQUENCE). Run the app.
๐ ๏ธ Solutions (5 Ways to Fix)
Use merge() instead of persist()
๐ Use this if you are using `EntityManager` directly and the object already has an ID.
The `merge()` method tells Hibernate to copy the state of your detached object onto the object in the database (or insert it if it doesn't exist).
// Instead of entityManager.persist(user);
entityManager.merge(user);Remove the manual ID assignment
๐ Use this if you are creating a NEW record but accidentally set the ID yourself.
If your `@Id` is annotated with `@GeneratedValue`, Hibernate will assign the ID. You must not set it manually before saving.
User user = new User();
user.setName("Deva");
// user.setId(1L); <-- REMOVE THIS
repository.save(user); // Hibernate generates the IDCheck for cascading relationships
๐ Use this if you are saving a parent object, and the child object has a manually set ID.
If `User` has `@OneToMany(cascade = CascadeType.PERSIST)` to `Order`, and your `Order` object has an ID set, Hibernate will try to persist it and fail. Use `CascadeType.MERGE` instead.
@Entity
public class User {
@OneToMany(cascade = CascadeType.MERGE) // Change from PERSIST to MERGE
private List<Order> orders;
}Fetch the entity before updating
๐ Use this if you are receiving a JSON payload to update an existing record.
Instead of passing the detached JSON object to `save()`, fetch the managed entity from the DB, update its fields, and save it.
@Transactional
public void updateUser(Long id, UserRequest req) {
User managedUser = repo.findById(id).orElseThrow();
managedUser.setName(req.getName());
repo.save(managedUser); // Safe, it's a managed entity
}Fix @GeneratedValue configuration
๐ Use this if you are using a SEQUENCE or TABLE generator but misconfigured it.
Ensure your `@GeneratedValue` strategy is correct. If it's set to `NONE`, Hibernate expects you to assign the ID, but if you don't, it might treat it as detached.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // Ensure strategy is correct
private Long id;๐ Version Notes
Uses Hibernate 5. Throws PersistentObjectException.
Uses Hibernate 6. Identical behavior.
๐ก๏ธ How to Prevent This Next Time
Never manually set the `@Id` field on new entities. Use Spring Data JPA's `save()` method, which internally checks for IDs and routes to `persist()` or `merge()` automatically.