hibernate/hibernate-orm · error · ObjectDeletedException

Given entity was removed

Error message

Given entity was removed

What it means

getCurrentLockMode() reads the EntityEntry of the object; if its status is DELETED/GONE the entity has been removed in this session (remove() scheduled or already flushed), and Hibernate throws ObjectDeletedException('Given entity was removed'). A deleted entity has no ongoing lock semantics — asking for its lock mode is a state error, not a missing-data error.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java:559

	@Override
	public LockMode getCurrentLockMode(Object object) {
		checkOpen();
		checkTransactionSyncStatus();
		if ( object == null ) {
			throw new NullPointerException( "null object passed to getCurrentLockMode()" );
		}

		final var lazyInitializer = extractLazyInitializer( object );
		if ( lazyInitializer != null ) {
			object = lazyInitializer.getImplementation( this );
			if ( object == null ) {
				return LockMode.NONE;
			}
		}

		final var entry = getEntityEntry( object );
		if ( entry.getStatus().isDeletedOrGone() ) {
			throw new ObjectDeletedException( "Given entity was removed", entry.getId(),
					entry.getPersister().getEntityName() );
		}
		else {
			return entry.getLockMode();
		}
	}

	@Override
	public Object getEntityUsingInterceptor(@Nonnull EntityKey key) {
		checkOpenOrWaitingForAutoClose();
		// todo : should this get moved to PersistentContext?
		// logically, is PersistentContext the "thing" to which an interceptor gets attached?
		final Object result = persistenceContext.getEntity( key );
		if ( result == null ) {
			final Object newObject = callInterceptorCallback(
					() -> getInterceptor().getEntity( key.getEntityName(), key.getIdentifier() ) );
			if ( newObject != null ) {
				lock( newObject, LockMode.NONE );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Skip lock inspection for removed entities: check entry status via session.getPersistenceContext().getEntry(obj) or track removed identities in a Set
  2. Reorder the flow: capture getCurrentLockMode() before remove(), not after
  3. If the entity must survive, avoid the remove or clear/reattach a different instance
  4. Catch ObjectDeletedException in generic monitoring code and treat it as 'no lock mode (deleted)'

Example fix

// before
session.remove(order);
logLock(session.getCurrentLockMode(order)); // ObjectDeletedException

// after
LockMode mode = session.getCurrentLockMode(order); // inspect first
session.remove(order);
logLock(mode);
Defensive patterns

Strategy: try-catch

Validate before calling

// Skip deleted entities before asking for their lock mode
EntityEntry entry = session.getPersistenceContext().getEntry(object);
if (entry == null || entry.getStatus().isDeletedOrGone()) {
    return LockMode.NONE; // or skip audit record
}
return session.getCurrentLockMode(object);

Try / catch

try {
    return session.getCurrentLockMode(object);
} catch (ObjectDeletedException e) {
    // entity already removed in this session — no lock mode exists
    LOG.debug("lock mode requested for deleted entity {}", e.getEntityName());
    return LockMode.NONE;
}

Prevention

When it happens

Trigger: Calling session.getCurrentLockMode(entity) after session.remove(entity) (or orphanRemoval marking it) within the same session/transaction, before or after flush. Also via cascade-delete: a child deleted by cascade then queried for lock mode.

Common situations: Audit/logging code that records lock modes for all processed entities and runs after a delete branch; generic frameworks intercepting remove() and then inspecting lock state; flows where the same object instance is reused for delete and later inspection in one transaction.

Related errors


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