{"record":{"id":"9103c550ddad16ba","repo":"hibernate/hibernate-orm","slug":"blobs-are-not-cacheable","errorCode":null,"errorMessage":"Blobs are not cacheable","messagePattern":"Blobs are not cacheable","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BlobJavaType.java","lineNumber":55,"sourceCode":"public class BlobJavaType extends AbstractClassJavaType<Blob> {\n\tpublic static final BlobJavaType INSTANCE = new BlobJavaType();\n\n\tpublic static class BlobMutabilityPlan implements MutabilityPlan<Blob> {\n\t\tpublic static final BlobMutabilityPlan INSTANCE = new BlobMutabilityPlan();\n\n\t\t@Override\n\t\tpublic boolean isMutable() {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic Blob deepCopy(Blob value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic Serializable disassemble(Blob value, SharedSessionContract session) {\n\t\t\tthrow new UnsupportedOperationException( \"Blobs are not cacheable\" );\n\t\t}\n\n\t\t@Override\n\t\tpublic Blob assemble(Serializable cached, SharedSessionContract session) {\n\t\t\tthrow new UnsupportedOperationException( \"Blobs are not cacheable\" );\n\t\t}\n\t}\n\n\tpublic BlobJavaType() {\n\t\tsuper( Blob.class, BlobMutabilityPlan.INSTANCE, IncomparableComparator.INSTANCE );\n\t}\n\n\t@Override\n\tpublic boolean isInstance(Object value) {\n\t\treturn value instanceof Blob;\n\t}\n\n\t@Override","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BlobJavaType.java#L37-L73","documentation":"BlobJavaType's mutability plan deliberately refuses to disassemble java.sql.Blob values: a Blob is a live handle on the JDBC connection/locator and cannot be serialized into the second-level cache. When an entity holding a Blob attribute is put into the shared cache, disassemble throws this UnsupportedOperationException ('Blobs are not cacheable').","triggerScenarios":"An entity with a java.sql.Blob attribute is annotated @Cacheable / included in a @Cache region; the first flush/load that writes the entity into the second-level cache invokes disassemble and fails.","commonSituations":"Turning on second-level cache (or adding the entity to a region) for legacy entities that map LOBs as java.sql.Blob; enabling query caching which caches entity identifiers/data; moving from byte[] to Blob mapping for streaming while keeping the cache annotation.","solutions":["Map the attribute as byte[] instead of java.sql.Blob (fully materialized, cacheable)","Keep Blob but exclude the entity from the second-level cache (remove @Cacheable/@Cache)","Split the LOB into a separate non-cached entity and cache only the metadata part","Alternatively use a converter that materializes byte[] for the cached form"],"exampleFix":"// before\n@Entity\n@Cacheable\n@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)\npublic class Attachment {\n    @Id private Long id;\n    private Blob content; // disassemble -> 'Blobs are not cacheable'\n}\n// after\n@Entity\n@Cacheable\n@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)\npublic class Attachment {\n    @Id private Long id;\n    private byte[] content; // materialized, cache-safe\n}","handlingStrategy":"fallback","validationCode":"// startup audit: no cached entity may hold java.sql.Blob/Clob attributes\nfor (Class<?> entity : annotatedEntities) {\n    if (entity.isAnnotationPresent(Cacheable.class)\n            || entity.isAnnotationPresent(org.hibernate.annotations.Cache.class)) {\n        for (Field f : entity.getDeclaredFields()) {\n            if (Blob.class.isAssignableFrom(f.getType()) || Clob.class.isAssignableFrom(f.getType())) {\n                throw new MappingException(\"Cached entity \" + entity + \" has LOB field \" + f);\n            }\n        }\n    }\n}","typeGuard":"static boolean isCacheSafeLobMapping(Class<?> fieldType) {\n    return !java.sql.Blob.class.isAssignableFrom(fieldType)\n        && !java.sql.Clob.class.isAssignableFrom(fieldType); // byte[]/String are fine\n}","tryCatchPattern":"// no meaningful retry: catch to convert into a clear configuration error\ntry {\n    session.persist(attachment);\n} catch (UnsupportedOperationException e) {\n    if (String.valueOf(e.getMessage()).contains(\"Blobs are not cacheable\")) {\n        throw new MappingException(\"Remove \" + Attachment.class.getSimpleName()\n            + \" from the 2nd-level cache or map its LOB as byte[]\", e);\n    }\n    throw e;\n}","preventionTips":["Map LOBs as byte[] when the entity is cached or the payload is small","Reserve java.sql.Blob for streaming large LOBs on deliberately non-cached entities","Audit cached entities for Blob/Clob/NClob fields whenever enabling 2LC","Split large LOBs into child entities and cache the parent only"],"tags":["hibernate","orm","second-level-cache","blob","lob","caching"],"backgroundTag":"second-level-cache-unsupported-type","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}