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; }
就能正確反序列化了
oracle blob 反序列化錯誤