oracle blob 反序列化错误
代码的目的是先将一个配置类JobConfig序列化存进Oracle中的Blob中,然后查的时候反序列化出来。
先看一下控制台报错
### Cause: com.audaque.lib.core.exception.AdqRuntimeException: error on getResult; nested exception is java.io.StreamCorruptedException: invalid stream header: 00540001 at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:107) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98) at sun.reflect.GeneratedMethodAccessor133.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:354) ... 84 more Caused by: com.audaque.lib.core.exception.AdqRuntimeException: error on getResult; nested exception is java.io.StreamCorruptedException: invalid stream header: 00540001 at com.audaque.datadiscovery.mybatis.SerializeHandler.getResult(SerializeHandler.java:50) at org.apache.ibatis.executor.resultset.FastResultSetHandler.getPropertyMappingValue(FastResultSetHandler.java:325) at org.apache.ibatis.executor.resultset.FastResultSetHandler.applyPropertyMappings(FastResultSetHandler.java:301) at org.apache.ibatis.executor.resultset.NestedResultSetHandler.getRowValue(NestedResultSetHandler.java:135) at org.apache.ibatis.executor.resultset.NestedResultSetHandler.handleRowValues(NestedResultSetHandler.java:102) at org.apache.ibatis.executor.resultset.FastResultSetHandler.handleResultSet(FastResultSetHandler.java:188) at org.apache.ibatis.executor.resultset.NestedResultSetHandler.handleResultSet(NestedResultSetHandler.java:73)
Mybatis resultMap
<resultMap type="com.audaque.datadiscovery.job.model.po.Job" id="Job"> <id property="jobId" column="JOB_ID" /> <result property="jobName" column="JOB_NAME" /> <result property="createTime" column="CREATE_TIME" /> <result property="description" column="DESCRIPTION" /> <result property="executeTime" column="EXECUTETIME" /> <result property="jobConfig" column="JOB_CONFIG" typeHandler="com.audaque.datadiscovery.mybatis.SerializeHandler" /> <association property="creator" columnPrefix="creator_" resultMap="User" /> </resultMap>
报错原因是查询后设置结果时,Job对象的JobConfig属性反序列化失败。
使用的是MyBatis框架,针对这个JobConfig 配置类,做了一个TypeHanlder,下面是这个TypeHandlder
public class SerializeHandler implements TypeHandler<Object> {
@Override
public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
ps.setString(i, null);
return;
}
try {
byte[] ss = SerializeUtils.serializeObject(parameter);
ps.setBytes(i, ss);
} catch (IOException e) {
throw new AdqRuntimeException("error on setParameter" ,e);
}
}
@Override
public Object getResult(ResultSet rs, String columnName) throws SQLException {
Object object = null;
try {
//反序列化报错
object = SerializeUtils.deserializeObject(rs.getBytes(columnName));
} catch (IOException e) {
throw new AdqRuntimeException("error on getResult" ,e);
} catch (ClassNotFoundException e) {
throw new AdqRuntimeException("error on getResult" ,e);
}
return object;
}
@Override
public Object getResult(CallableStatement cs, int columnIndex)
throws SQLException {
Object object = null;
try {
object = SerializeUtils.deserializeObject(cs.getBytes(columnIndex));
} catch (IOException e) {
throw new AdqRuntimeException("error on getResult" ,e);
} catch (ClassNotFoundException e) {
throw new AdqRuntimeException("error on getResult" ,e);
}
return object;
}
@Override
public Object getResult(ResultSet rs, int columnIndex) throws SQLException {
Object object = null;
try {
object = SerializeUtils.deserializeObject(rs.getBytes(columnIndex));
} catch (IOException e) {
throw new AdqRuntimeException("error on getResult" ,e);
} catch (ClassNotFoundException e) {
throw new AdqRuntimeException("error on getResult" ,e);
}
return object;
}
}
TypeHandler中有一个序列化工具类
public final class SerializeUtils { /** * * @param object is want to serialize * @return * @throws IOException */ public static <T> byte[] serializeObject(T object) throws IOException { byte[] buffer = null; ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; long start = System.currentTimeMillis(); try{ bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(object); oos.flush(); buffer = bos.toByteArray(); }catch(IOException ex){ throw ex; }finally{ if(oos != null){ oos.close(); } if(bos != null){ bos.close(); } long end = System.currentTimeMillis(); // System.out.println("serializeObject "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(start))+" costs "+(end-start) + " ms"); } return buffer; } /** * * @param buf is want to deserialize * @return * @throws IOException * @throws ClassNotFoundException */ @SuppressWarnings({ "unchecked" }) public static <T> T deserializeObject(byte[] buf) throws IOException, ClassNotFoundException { T object = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; long start = System.currentTimeMillis(); try{ bis = new ByteArrayInputStream(buf); ois = new ObjectInputStream(bis); object = (T) ois.readObject(); }catch(IOException ex){ throw ex; }finally{ if(ois != null){ ois.close(); } if(bis != null){ bis.close(); } long end = System.currentTimeMillis(); // System.out.println("deserializeObject "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(start))+" costs "+(end-start) + " ms"); } return object; }
我先测试了工具类SerializeUtils是否有问题,测试了一下是没有问题的。
我在序列化之后调用反序列化方法,也是没有问题的。
问题出在从数据库查询出来后,查了一下读取BLOB对象为byte的java代码,java.sql.Blob对象转化byte数组 和 oracle.sql.Blob对象转化byte数组的方法不同,如果使用 oracle.sql.Blob.getBytes方法转化,则会报java.io.StreamCorruptedException: invalid stream header: 006C0001 错误
QueryRunner run = new QueryRunner(true); String querySql = "SELECT JOB_CONFIG FROM adqm_job where job_id = 141"; Object[] array = run.query(con, querySql, new ArrayHandler()); Blob blob= (Blob) array[0]; byte[] returnValue = blob.getBytes(1, (int) blob.length()); System.out.println(Arrays.toString(returnValue)); InputStream is = null; BLOB blob1 = (BLOB)(array[0]); byte[] b = null; try { is = blob1.getBinaryStream(); b = new byte[(int) blob1.length()]; is.read(b); } catch (Exception e) { e.printStackTrace(); }
但是系统代码使用的是 rs.getBytes(columnName)代码,那是否是getBytes代码有问题吗?
Connection con = DBUtil.getConnection(); String querySql = "SELECT JOB_CONFIG FROM adqm_job where job_id = 141"; PreparedStatement preparedStatement = con.prepareStatement(querySql); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { byte []bytes= resultSet.getBytes("JOB_CONFIG"); SerializeUtils.deserializeObject(bytes); } DBUtil.close(con);
测试了一下使用 rs.getBytes(columnName) 取出byte数组是 反序列化是没有问题的。
BUG的原因还是没有找到,我猜想的原因是object = SerializeUtils.deserializeObject(rs.getBytes(columnName)); 中rs.getBytes(columnName)的底层实现为((BLOB)(rs.getBlob)).getBytes();
最后我将TypeHandler 的getResult方法中改为
@Override public Object getResult(ResultSet rs, String columnName) throws SQLException { Object object = null; // 集成工作流BUG修复 try { Blob blob = rs.getBlob(columnName); byte[] returnValue = null; if (null != blob) { returnValue = blob.getBytes(1, (int) blob.length()); object = SerializeUtils.deserializeObject(returnValue); } // 代码 // object = SerializeUtils.deserializeObject(rs.getBytes(columnName)); } catch (IOException e) { throw new AdqRuntimeException("error on getResult" ,e); } catch (ClassNotFoundException e) { throw new AdqRuntimeException("error on getResult" ,e); } return object; }
就能正确反序列化了