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
- Treat natural ids as immutable by design: remove setters for the @NaturalId field and re-create entities when the key changes.
- 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.
- 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
- Model natural keys as immutable values; 'change' means delete + insert.
- Add @Column(updatable = false) on @NaturalId fields so Hibernate itself rejects updates.
- Integration-test every @NaturalId field for attempted mutation.
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
- Unable to update read-only object
- Unable to update read-only object
- Unsupported access strategy: " + accessType
- Unable to update read-only object
- Unsupported access strategy: " + accessType
AI-assisted analysis of redisson/redisson@91188987c2 (2026-08-14).
Data as JSON: /api/errors/9e654eeb410c7f5e.
Report an issue: GitHub.