hibernate/hibernate-orm · error · NonUniqueObjectException

A different object with the same identifier value was alread

Error message

A different object with the same identifier value was already associated with this persistence context

What it means

While preparing a save, AbstractSaveEventListener.entityKey() computes the EntityKey for the generated/assigned id and finds a different object instance already managed under that key in the persistence context (and not scheduled for deletion). It throws NonUniqueObjectException: within one Session only one instance per identifier may exist, and you are attaching a second copy via save()/update()/persist().

Source

Thrown at hibernate-core/src/main/java/org/hibernate/event/internal/AbstractSaveEventListener.java:248

		}
		else {
			assert id != null;
			key = entityKey( id, persister, source );
		}
		return performSaveOrReplicate( entity, key, persister, useIdentityColumn, context, source, delayIdentityInserts );
	}

	@Nonnull
	private static EntityKey entityKey(@Nonnull Object id, @Nonnull EntityPersister persister, @Nonnull EventSource source) {
		final var key = source.generateEntityKey( id, persister );
		final var persistenceContext = source.getPersistenceContextInternal();
		final Object old = persistenceContext.getEntity( key );
		if ( old != null ) {
			if ( persistenceContext.getEntry( old ).getStatus() == Status.DELETED ) {
				source.forceFlush( persistenceContext.getEntry( old ) );
			}
			else {
				throw new NonUniqueObjectException( id, persister.getEntityName() );
			}
		}
		else if ( persistenceContext.containsDeletedUnloadedEntityKey( key ) ) {
			source.forceFlush( key );
		}
		return key;
	}

	/**
	 * Performs all the actual work needed to persist an entity
	 * (well to get the persist action moved to the execution queue).
	 *
	 * @param entity The entity to be persisted
	 * @param key The id to be used for saving the entity (or null, in the case of identity columns)
	 * @param persister The persister for the entity
	 * @param useIdentityColumn Should an identity column be used for id generation?
	 * @param context Generally cascade-specific information
	 * @param source The session which is the source of the current event

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use merge() instead of update()/save() for detached instances — merge copies state onto the managed copy
  2. session.evict(managedInstance) or session.clear() before reattaching if you truly need the new instance
  3. Keep one Session per unit of work so detached copies never get attached alongside managed ones
  4. Load the entity in the same session and modify the managed instance instead of reattaching

Example fix

// before: session already manages Customer#7
Customer detached = copyFromDto(dto); // different instance, same id
session.update(detached); // NonUniqueObjectException

// after
session.merge(detached); // state copied onto the managed instance
Defensive patterns

Strategy: try-catch

Validate before calling

// detect a managed instance with the same id before reattaching
Object managed = session.find(Customer.class, detached.getId());
if (managed != null && managed != detached) {
    // decide: merge, or evict the managed instance
    session.merge(detached);
    return;
}
session.update(detached);

Try / catch

try {
    session.update(detached);
} catch (NonUniqueObjectException e) {
    // another instance with the same id is already managed in this session
    session.merge(detached); // copies state onto the managed instance
}

Prevention

When it happens

Trigger: session.update(detachedCopy) while a different instance with the same id is already managed in the session; save() with an explicitly set id that collides with an already-loaded entity; a fresh detached copy built from a DTO reattached next to the managed original in the same session.

Common situations: Load-modify in one session, then also update() a detached copy of the same row; passing entities across sessions and reattaching them; import/sync routines re-saving existing rows.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/73f12bcd57952bb7. Report an issue: GitHub.