Hibernate NonUniqueObjectException求助:关联已有警员至多张罚单时报错
Hey there! Let's break down why you're running into that NonUniqueObjectException and how to fix it. This error usually pops up when Hibernate's Session detects two different object instances with the same database identifier (OID) — which violates its first-level cache rules, and it's super common when reusing existing entities for associations like your Cop-Ticket scenario.
Common Causes in Your Scenario
- Re-saving a persistent entity: If you've already loaded a Cop from the database (it's in persistent state within the current Session) and then call
save()orpersist()on it again, Hibernate freaks out because it already tracks that instance. - Using a detached entity for association: If your Cop instance comes from a closed Session (it's detached) and you try to associate it with a new Ticket in a fresh Session, Hibernate might already have another instance of that Cop loaded, causing a conflict.
- Incorrect DAO methods: Your DAO might be using
save()universally, even for entities that already exist in the database.
Step-by-Step Solutions
1. Match Method to Entity State
Hibernate entities have three core states, and you need to use the right method for each:
- Transient (new entity, no ID): Use
save()orpersist()to create it in the database. - Persistent (already tracked by Session): You don't need to call
save()at all! Just modify the entity's properties, and Hibernate will automatically sync changes to the database when the Session flushes. - Detached (was persistent, but Session closed): Use
merge()to copy the detached entity's state into a persistent instance that Hibernate can track.
2. Update Your DAO Logic
Tweak your DaoCop (and Ticket DAO) to handle existing entities properly. For example:
public class DaoCop { private Session session; public DaoCop(Session session) { this.session = session; } public Cop saveOrUpdateCop(Cop cop) { if (cop.getId() == null) { // New entity, save to database session.save(cop); } else { // Existing entity: merge if detached, sync if persistent cop = (Cop) session.merge(cop); } return cop; } // Load existing Cop by ID (returns persistent entity tracked by Session) public Cop getCopById(Long copId) { return session.get(Cop.class, copId); } }
3. Properly Associate Existing Cop with Multiple Tickets
When linking an existing Cop to new Tickets, make sure you're using a persistent instance of the Cop in the current Session:
// Open a new Session and start transaction Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); try { DaoCop daoCop = new DaoCop(session); DaoTicket daoTicket = new DaoTicket(session); // Load the existing Cop (persistent state, tracked by current Session) Cop existingCop = daoCop.getCopById(1L); // First Ticket: associate with the persistent Cop Ticket ticket1 = new Ticket(); ticket1.setCop(existingCop); daoTicket.saveOrUpdateTicket(ticket1); // Second Ticket: use the SAME persistent Cop instance Ticket ticket2 = new Ticket(); ticket2.setCop(existingCop); daoTicket.saveOrUpdateTicket(ticket2); tx.commit(); } catch (Exception e) { tx.rollback(); e.printStackTrace(); } finally { session.close(); }
If your Cop instance is detached (from a previous closed Session), merge it first before associating:
// Detached Cop from a closed Session Cop detachedCop = ...; // Merge into current Session to get a persistent instance Cop persistentCop = (Cop) session.merge(detachedCop); // Now associate persistentCop with Tickets as shown above
Key Takeaways
- Never call
save()on an entity that already exists in the database unless it's a brand-new transient instance. - Always use persistent entities (loaded in the current Session) for associations to avoid conflicts.
- Use
merge()for detached entities to sync their state with the Session's tracked instance.
内容的提问来源于stack exchange,提问作者Riley Christian

