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
- Switch the entity to CacheConcurrencyStrategy.READ_WRITE or NONSTRICT_READ_WRITE if it is mutable
- Keep the entity immutable and remove the update path instead of fighting the strategy
- For bulk-replaced data, evict the region after loading the new dataset rather than per-row updates
- 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
- Reserve READ_ONLY strategy for truly immutable entities
- Run a startup audit flagging mutable entities mapped with READ_ONLY
- Review the global default cache strategy whenever write paths are added
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
- Can't write to a read-only object
- cannot recreate collection while filter is enabled: " + coll
- Instance of '" + entityName + "' references an unsaved trans
- Action was vetoed: " + entityAction
- Decomposition not supported for %s
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/00a7142bbc733bfd.
Report an issue: GitHub.