hibernate/hibernate-orm · error · UnsupportedOperationException

Can't update read-only object

Error message

Can't update read-only object

What it means

The read-only cache concurrency strategy assumes cached entities never change, so EntityReadOnlyAccess implements update() by throwing UnsupportedOperationException. When a session flushes modifications to an entity whose region uses the read-only strategy, the second-level cache callback reaches this method and the flush fails. It is the strategy telling you the mapping contract (immutable data) was violated.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/cache/spi/support/EntityReadOnlyAccess.java:81

	}

	@Override
	public void unlockItem(
			@Nonnull SharedSessionContractImplementor session,
			@Nonnull Object key,
			@Nullable SoftLock lock) {
		evict( key );
	}

	@Override
	public boolean update(
			@Nonnull SharedSessionContractImplementor session,
			@Nonnull Object key,
			@Nonnull Object value,
			@Nullable Object currentVersion,
			@Nullable Object previousVersion) {
//		LOG.debugf( "Illegal attempt to update item cached as read-only [%s]", key );
		throw new UnsupportedOperationException( "Can't update read-only object" );
	}

	@Override
	public boolean afterUpdate(
			@Nonnull SharedSessionContractImplementor session,
			@Nonnull Object key,
			@Nonnull Object value,
			@Nullable Object currentVersion,
			@Nullable Object previousVersion,
			@Nullable SoftLock lock) {
//		LOG.debugf( "Illegal attempt to update item cached as read-only [%s]", key );
		throw new UnsupportedOperationException( "Can't write to a read-only object" );
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Switch the entity to CacheConcurrencyStrategy.READ_WRITE or NONSTRICT_READ_WRITE if it is mutable
  2. Keep the entity immutable and remove the update path instead of fighting the strategy
  3. For bulk-replaced data, evict the region after loading the new dataset rather than per-row updates
  4. Fix a bad global default: hibernate.cache.default_cache_concurrency_strategy must not be read-only for mutable entities

Example fix

// before
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Product { ... } // product.setPrice(p); em.flush(); -> throws

// after
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product { ... }
Defensive patterns

Strategy: validation

Validate before calling

org.hibernate.annotations.Cache cache =
        Product.class.getAnnotation(org.hibernate.annotations.Cache.class);
if (cache != null && cache.usage() == CacheConcurrencyStrategy.READ_ONLY) {
    throw new IllegalStateException(
            "Refusing to update entity cached with read-only strategy: Product");
}

Try / catch

try {
    session.flush();
} catch (UnsupportedOperationException e) {
    if ("Can't update read-only object".equals(e.getMessage())) {
        throw new IllegalStateException(
                "Entity is cached READ_ONLY but was modified - fix the @Cache usage", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Mapping an entity with @Cache(usage = READ_ONLY) and then modifying instances and flushing; setting hibernate.cache.default_cache_concurrency_strategy=read-only globally while some entities are mutable; import or admin jobs updating reference data that is cached read-only.

Common situations: 'Reference data' cached READ_ONLY that later becomes editable through new features; a global read-only default chosen for performance; copying the cache annotation from an immutable entity onto a mutable one.

Related errors


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