Hibernate操作Blob/Clob时,发生cannot be cast to oracle.sql.BLOB错误分析

作者:隔壁老王(wallimn.iteye.com),本人原创,欢迎转载,转载请保留此信息。2010-06-19

  环境:Hibernate3.5.2、jdk1.6、Oracle9.2.0.8、odbc14(9.2.0.8).jar、连接池proxool0.9.1、tomcat6.0

  操作Blob时,对Hibernate返回的BLOB类型的实例blob进行强制类型转换时,
  即:(oracle.sql.BLOB)blob,
  发生类型转换错误(java.lang.ClassCastException),提示信息为cannot be cast to oracle.sql.BLOB,非常奇怪,这似乎不应该呀。

  简单看了一下Hibernate的相关源码,原来由Hibernate返回的blob对象实际上是代理类SerializableBlobProxy的实例,提供了java.sql.Blob接口定义的方法(实际上是通过反射的方式),表现得像是个实现java.sql.Blob接口的对象(只是貌似),但实际上是个Proxy类型的实例,因此不能把这个实例转化成oracle.sql.BLOB类型。

  当用System.out.println(blob)输出信息的时候,显示的是oracle.sql.BLOB@10fba68,很具迷惑性。但使用System.out.println(blob.getClass().getName())输出信息的时候,显示的是$Proxy6,这显示了真实的情况,也就是说显示了这个实例的真实面目。

  一句话,这个实例是个穿着java.sql.Blob马甲的Proxy类型的实例。

  原因找到了,解决起来也就不难了。不必困扰于为什么oracle.sql.BLOB类型的实例不能强制转化成oracle.sql.BLOB类型这令人费解的问题了。

  解决方法很简单,不过是我花了一整天的时间找到的。

Java代码   收藏代码
  1. SerializableBlobProxy proxy = (SerializableBlobProxy )Proxy.getInvocationHandler(blob);  
  2. java.sql.Blob realBlob = proxy.getWrappedBlob();  

  这样得到的realBlob,是个真正的oracle.sql.BLOB对象,可以进行类型转化了。

  另外:
  经测试,对realBlob实例执行setBinaryStream(1L)方法时,会报异常。Oracle不支持这个方法,反编译odbc14.jar看了一下源码,调用这个方法,会直接抛出异常。

  不使用hibernate时,也可能会遇到这个问题。估计只要系统中用到动态代理技术,都有可能遇到这个怪异的类型转化异常(java.lang.ClassCastException)问题。解决方法应该是类似的。

  还有,如果使用tomcat容器以及容器带的DBCP连接池,当tomcat的共享包目录中放的jdbc包与工程中带的jdbc包的版本不一致的时候,也可能引发这个错误。这种情况下,只要把工程中带的jdbc包删掉即可。


-=------------------------------------------------------------补充

对于使用jdbc查得的blob转换报的异常,据说与服务器的jdbc包有关。


======================================================================================================

Java EE 容器的类加载


Java EE 应用常以 EAR 格式来部署,这种格式也相较于 WAR 复杂,所以就只以 EAR 为例介绍了。

EAR 包中可以存在一个或多个 EJB-JAR 包和同样可以一个或多个的 WAR 包。EAR 包在 Java EE 容器中会有自己的类加载器,同时 EJB-JAR 和 WAR 也有自己的类加载器。EAR 包的类加载器会是 其 EJB-JAR 包和 WAR 包类加载器的父加载器。如果 WAR 包依赖某个 EJB-JAR 包,那这两者的类加载器会成父子关系。

JBoss AS 7 的类加载的小特殊


JBoss AS 7 在默认的配置下,其类加载机制和上述 Java EE 容器的类加载有所不同。默认情况下,JBoss AS 中一个 EAR 包的 WAR 和 EJB-JAR 的类加载器对互相的类都是可见的。这个配置可以通过修改 standalone.xml 来实现,加入如下配置:

1<subsystemxmlns="urn:jboss:domain:ee:1.0">           
2    <ear-subdeployments-isolated>false</ear-subdeployments-isolated>
3</subsystem>


======================================================================================================


估计因为这样一个原因,由于ojdbcxx.jar和xx.ear发布在不同的项目中,导致oracle.sql.CLOB无法强制转换成oracle.sql.CLOB。

两个类的classloader的打印信息:
c.getClassLoader()----------------ModuleClassLoader for Module "deployment.ojdbc6-oracle-11g.jar:main" from Service Module Loader

oracle.sql.CLOB.class.getClassLoader()-------------ModuleClassLoader for Module "deployment.btpcs.ear:main" from Service Module Loader

直接采用反射去转换:

                         if (obj.getClass().getName().equals("oracle.sql.CLOB")) {
                                Class c = obj.getClass();
                                Method m = c.getMethod("stringValue");
                                obj = m.invoke(obj);
                            } 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值