又被古董级的库坑了—— ibatis typehandler

为了将一个不知道什么类型的java对象存到数据库里面,用了个很恶心的方法:将对象序列化之后,保存到数据库的blob对象。这样整个序列化和反序列化过程,都期望在ibatis中完成,上层就能直接获取到需要的对象了。
第一次安装网上的方式,想直接参考spring的BlobByteArrayTypeHandler,将blob对象保存到数据库。因为这个类是需要的是byte数组,还不满足我的需求,就参照这个类,继承了AbstractLobTypeHandler,重写getResultInternal和setParameterInternal。然后将这个type handler配置到ibatis的sqlmap文件中。一切看上去很顺利,结果启动的时候ibatis报ClassCastException,说这个handler无法强制转换成TypeHandlerCallback。

这个异常看上去很奇怪,去网上搜索了InlineParameterMapParser类,里面的逻辑大致是:
[cce lang="java"]
} else if ("handler".equals(field)) {
try {
value = typeHandlerFactory.resolveAlias(value);
Object impl = Resources.instantiate(value);
if (impl instanceof TypeHandlerCallback) {
mapping.setTypeHandler(new CustomTypeHandler((TypeHandlerCallback) impl));
} else if (impl instanceof TypeHandler) {
mapping.setTypeHandler((TypeHandler) impl);
} else {
throw new SqlMapException ("The class " + value + " is not a valid implementation of TypeHandler or TypeHandlerCallback");
}
} catch (Exception e) {
throw new SqlMapException("Error loading class specified by handler field in " + token + ". Cause: " + e, e);
}
[/cce]
也就是说,如果在sql中配置的handler是同时兼容TypeHandlerCallback和TypeHandler接口的,spring的AbstractLobTypeHandler类,实现的是BaseTypeHandler,应该没有问题的啊。

反编译了应用中试用的ibatis(版本是2.1.6),发现里面的逻辑完全不一样:
[cce lang="java"]
} else if ("handler".equals(field)) {
try {
value = typeHandlerFactory.resolveAlias(value);
mapping.setTypeHandler(new CustomTypeHandler((TypeHandlerCallback)Resources.classForName(value).newInstance()));
} catch (Exception e) {
throw new SqlMapException("Error loading class specified by handler field in " + token + ". Cause: " + e, e);
}
} else {
throw new SqlMapException("Unrecognized parameter mapping field: '" + field + "' in " + token);
}
[/cce]
这个古董是直接初始化之后强转为TypeHandlerCallback的。没办法,只能自己实现这个接口。

[cce lang="java"]
public class JavaSerializeBlobHandler implements TypeHandlerCallback {

/*
* (non-Javadoc)
* @see com.ibatis.sqlmap.engine.type.TypeHandler#valueOf(java.lang.String)
*/
public Object valueOf(String s) {
return s;
}

/*
* (non-Javadoc)
* @see
* com.ibatis.sqlmap.client.extensions.TypeHandlerCallback#getResult(com.ibatis.sqlmap.client.extensions.ResultGetter
* )
*/
public Object getResult(ResultGetter paramResultGetter) throws SQLException {
byte[] buf = paramResultGetter.getBytes();
if(buf == null) {
return null;
}
try {
ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(buf));
return inputStream.readObject();
} catch (IOException e) {
throw new IllegalArgumentException(e);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}

/*
* (non-Javadoc)
* @see com.ibatis.sqlmap.client.extensions.TypeHandlerCallback#setParameter(com.ibatis.sqlmap.client.extensions.
* ParameterSetter, java.lang.Object)
*/
public void setParameter(ParameterSetter paramParameterSetter, Object paramObject) throws SQLException {
if (paramObject == null) {
paramParameterSetter.setNull(Types.BLOB);
} else {
try {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream);
objectOutputStream.writeObject(paramObject);

paramParameterSetter.setBytes(byteStream.toByteArray());
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}
}
[/cce]
不过接口中的valueOf方法,到底是干什么用的,什么时候调用的还没搞清楚。将这个类配置在sqlmap的resultMap或sql中内联,都能够正常的对对象进行存取。

发表评论

您的电子邮箱地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据