hibernate/hibernate-orm · error · UnsupportedOperationException

Clobs are not cacheable

Error message

Clobs are not cacheable

What it means

NClobMutabilityPlan implements disassemble() by throwing UnsupportedOperationException because a live java.sql.NClob is a handle tied to a JDBC connection/locator and cannot be serialized into the second-level cache. disassemble runs when Hibernate builds a cache entry, so the exception appears at write time (insert/update of a cached entity, or putting a query result into the cache).

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/NClobJavaType.java:49

 * @author Steve Ebersole
 * @author Loïc Lefèvre
 */
public class NClobJavaType extends AbstractClassJavaType<NClob> {
	public static final NClobJavaType INSTANCE = new NClobJavaType();

	public static class NClobMutabilityPlan implements MutabilityPlan<NClob> {
		public static final NClobMutabilityPlan INSTANCE = new NClobMutabilityPlan();

		public boolean isMutable() {
			return false;
		}

		public NClob deepCopy(NClob value) {
			return value;
		}

		public Serializable disassemble(NClob value, SharedSessionContract session) {
			throw new UnsupportedOperationException( "Clobs are not cacheable" );
		}

		public NClob assemble(Serializable cached, SharedSessionContract session) {
			throw new UnsupportedOperationException( "Clobs are not cacheable" );
		}
	}

	public NClobJavaType() {
		super( NClob.class, NClobMutabilityPlan.INSTANCE, IncomparableComparator.INSTANCE );
	}

	@Override
	public boolean isInstance(Object value) {
		return value instanceof NClob;
	}

	@Override
	public NClob cast(Object value) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Model the LOB in a separate, non-cached entity and reference it @OneToOne(fetch=LAZY); cache only the LOB-free aggregate
  2. Map the column as materialized text: @Lob @Nationalized String (String is cacheable) instead of java.sql.NClob
  3. Remove the entity/property from second-level caching entirely
  4. Verify no query cache stores entities with NClob attributes

Example fix

// before
@Entity @Cacheable
class Article {
    @Id Long id;
    @Nationalized java.sql.NClob body; // disassemble -> UnsupportedOperationException
}

// after
@Entity @Cacheable
class Article {
    @Id Long id;
    @OneToOne(fetch = FetchType.LAZY) ArticleBody body; // separate table
}
@Entity
class ArticleBody {
    @Id Long id;
    @Nationalized @Lob String body; // materialized, cacheable sibling
Defensive patterns

Strategy: validation

Validate before calling

// startup check: refuse caching entities with live LOB fields
for (var binding : sessionFactory.getMetamodel().getEntities()) {
    for (var attr : binding.getAttributes()) {
        if (java.sql.NClob.class.isAssignableFrom(attr.getJavaType())
                && binding.getJpaMetamodel()... /* entity is @Cacheable */) {
            throw new IllegalStateException("Cached entity " + binding.getName() + " has NClob attribute " + attr.getName());
        }
    }
}

Type guard

static boolean isCacheableJavaType(Class<?> t) {
    return !(java.sql.NClob.class.isAssignableFrom(t) || java.sql.Clob.class.isAssignableFrom(t) || java.sql.Blob.class.isAssignableFrom(t));
}

Try / catch

catch (UnsupportedOperationException e) {
    if ("Clobs are not cacheable".equals(e.getMessage()))
        throw new IllegalStateException("Remove NClob attributes from cached entities or map them as String", e);
    throw e;
}

Prevention

When it happens

Trigger: An entity with a java.sql.NClob (or @Nationalized LOB) attribute is annotated @Cacheable or covered by a @Cache(...) region; a collection or query cache entry includes such a value; hibernate.cache enabled globally and the entity gets cached on flush

Common situations: Adding caching annotations to legacy entities that carry LOB fields; moving LOB content from String columns to NClob columns on already-cached entities; enabling query caching on queries selecting LOB entities

Related errors


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