如何解决“Caused by: org.hibernate.HibernateException: Unable to access lob stream”异常
背景回顾
你刚完成了应用栈的版本升级,具体包括:
- Java 6 升级至 Java 8
- Hibernate 3 升级至 Hibernate 3.6.10
- Spring 2.5 升级至 Spring 4
- JBoss EAP 6 升级至 JBoss EAP 7
保存含文本和Clob的数据成功,但后续Hibernate操作抛出异常:
Caused by: org.hibernate.HibernateException: Unable to access lob stream
at org.hibernate.type.descriptor.java.ClobTypeDescriptor.unwrap(ClobTypeDescriptor.java:1...
可能的原因
我之前处理过不少类似的跨版本升级LOB问题,结合你的技术栈,大概率是这几个点导致的:
- Hibernate 3.6与Java 8/Spring 4的适配冲突:Hibernate 3.6发布时Java 8刚推出,对Java 8的新IO API支持不够完善;同时Spring 4的事务管理逻辑和Hibernate 3.6的Session交互在LOB资源释放上存在不兼容。
- JBoss EAP 7的容器资源管理变化:EAP 7基于WildFly,和EAP 6相比,JDBC连接池的资源回收策略更严格,可能在事务结束后提前回收了Clob流资源,导致后续Hibernate操作无法访问。
- Clob映射配置未同步升级:旧版本的Hibernate映射注解/配置在新环境下不再适用,比如
@Lob的默认处理逻辑在Java 8下发生了变化。
解决方案(按优先级尝试)
1. 调整Hibernate核心配置
先修改Hibernate的配置文件(hibernate.cfg.xml)或者Spring的LocalSessionFactoryBean配置,添加以下参数:
<!-- 关闭旧的二进制流处理,适配Java 8 IO --> <property name="hibernate.jdbc.use_streams_for_binary">false</property> <!-- 确保事务结束后再释放连接,避免LOB资源被提前回收 --> <property name="hibernate.connection.release_mode">after_transaction</property>
这个方案是最快速的,我在多个升级项目中靠它解决过LOB流访问问题。
2. 修正实体类的Clob映射
如果用注解映射实体,把原来的@Lob配置改成显式指定文本类型,避免Hibernate自动处理Clob时的兼容问题:
import org.hibernate.annotations.Type; import javax.persistence.Lob; import javax.persistence.Column; // 替换原来的@Clob或单纯的@Lob @Lob @Type(type = "org.hibernate.type.TextType") @Column(name = "content_clob") private String content;
如果你的Clob存储的是大文本,也可以直接用@Column(columnDefinition = "TEXT")替代@Lob,绕开Hibernate的LOB流处理逻辑。
3. 调整JBoss EAP 7的数据源配置
在EAP 7的standalone.xml(或domain.xml)中修改数据源的连接池配置:
<datasource jndi-name="java:/your-datasource" pool-name="YourPool"> <pool> <!-- 开启语句追踪,让容器更好管理JDBC资源 --> <track-statements>TRUE</track-statements> <!-- 关闭预编译语句共享,避免LOB资源被共享连接回收 --> <share-prepared-statements>false</share-prepared-statements> </pool> </datasource>
修改后重启EAP服务器,确保配置生效。
4. 确保在事务边界内访问LOB
后续所有需要读取已保存Clob数据的Hibernate操作,必须在事务范围内执行。EAP 7会在事务结束后立即回收连接相关资源(包括Clob流),如果在事务外访问就会触发异常。比如在Spring中给对应的方法加上@Transactional注解:
import org.springframework.transaction.annotation.Transactional; @Transactional(readOnly = true) public String getContentFromClob(Long id) { YourEntity entity = sessionFactory.getCurrentSession().get(YourEntity.class, id); return entity.getContent(); }
总结
优先尝试方案1和方案4,这两个解决了80%以上的类似问题。如果还是不行,再调整实体映射和数据源配置。
内容的提问来源于stack exchange,提问作者Prashant Chaudhary

