redisson/redisson · error · UnsupportedOperationException

Unable to update read-only object

Error message

Unable to update read-only object

What it means

Thrown by ReadOnlyNaturalIdRegionAccessStrategy.update() (Hibernate 5.2 module) when Hibernate tries to propagate an update of an entity's natural id into a natural-id cache region configured with a read-only strategy. Natural-id read-only regions support insert-time population (afterInsert) but no update path, so update() throws UnsupportedOperationException. The trigger is always that the natural id of a READ_ONLY-cached entity is being changed.

Source

Thrown at redisson-hibernate/redisson-hibernate-52/src/main/java/org/redisson/hibernate/strategy/ReadOnlyNaturalIdRegionAccessStrategy.java:83

    @Override
    public NaturalIdRegion getRegion() {
        return (NaturalIdRegion) region;
    }

    @Override
    public boolean insert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
        return false;
    }

    @Override
    public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
        region.put(session, key, value);
        return true;
    }

    @Override
    public boolean update(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
        throw new UnsupportedOperationException("Unable to update read-only object");
    }

    @Override
    public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, SoftLock lock) throws CacheException {
        throw new UnsupportedOperationException("Unable to update read-only object");
    }

    @Override
    public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SharedSessionContractImplementor session) {
        return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().createNaturalIdKey(naturalIdValues, persister, session);
    }

    @Override
    public Object[] getNaturalIdValues(Object cacheKey) {
        return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().getNaturalIdValues(cacheKey);
    }

}

View on GitHub (pinned to 91188987c2)

Solutions

  1. Treat natural ids as immutable by design: remove setters for the @NaturalId field and re-create entities when the key changes.
  2. If the natural id must be mutable, switch the owning entity's cache to a mutable strategy (@Cache(usage = NONSTRICT_READ_WRITE)) so the natural-id region gets a matching strategy.
  3. Add an integration test that attempts to change each @NaturalId field so regressions fail fast in CI, not in production.

Example fix

// before
@Entity
@NaturalIdCache
public class Employee { @NaturalId String employeeNo; void setEmployeeNo(String v){...} }

// after
@Entity
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@NaturalIdCache
public class Employee { @NaturalId String employeeNo; /* no setter: immutable key */ }
Defensive patterns

Strategy: validation

Validate before calling

// design-time rule: @NaturalId fields must have no setters
boolean naturalIdIsImmutable(Class<?> entity, java.lang.reflect.Field f) {
    return f.isAnnotationPresent(org.hibernate.annotations.NaturalId.class)
        && java.util.Arrays.stream(entity.getMethods())
            .noneMatch(m -> m.getName().equalsIgnoreCase("set" + f.getName()));
}

Try / catch

try {
    session.flush(); // natural id of READ_ONLY-cached entity was changed
} catch (UnsupportedOperationException e) {
    if ("Unable to update read-only object".equals(e.getMessage())) {
        // natural key changed: either treat key as immutable or use a mutable strategy
    }
    throw e;
}

Prevention

When it happens

Trigger: An entity with @NaturalId fields and @NaturalIdCache (region resolved with read-only usage) has its natural-id property modified and flushed — Hibernate invokes update(key, value) on the natural-id access strategy.

Common situations: Natural keys assumed immutable (email, SSN, SKU) that the application later allows editing; default read-only strategy applied globally while one entity legitimately renames its natural key.

Related errors


AI-assisted analysis of redisson/redisson@91188987c2 (2026-08-14). Data as JSON: /api/errors/9e654eeb410c7f5e. Report an issue: GitHub.