上一篇我们已经成功用jdbc搞定了XMLType类型,这里将讲解如何用Mybatis来替代jdbc操作。
使用Mybatis操作XMLType,我们同样在Java端映射为String类型,当直接操作不做任何处理时,和jdbc大体一样,传输的内容长度小于4000时一切正常,当传输的内容长度超过4000左右时,同样抛出异常:ORA-01461: can bind a LONG value only for insert into a LONG column。
可见,Mybatis的操作其实和jdbc是一样的,只不过它在jdbc的外面又封装了一层,使得我们可以采用配置文件等映射的方式来更方便的访问数据库,我们要做的,就是在原有Mybatis便捷性的基础上实现对XMLType类型数据的插入,这种情况下,实现一个XMLType类型的自定义TypeHandler处理器是最好的选择。关于mybatis自定义转换器的实现,请移步[Mybatis实现自定义的类型转换器TypeHandler]
这里,我们仍然采用前面提到的方案三,自然那两个jar包:xdb.jar,xmlparserv2.jar也是要加入的。
添加一个XmltypeTypeHandler,实现TypeHandler接口,由于插入数据主要用到setParameter方法,所以这里只列出该方法,其它方法代码略:
/** * oracle SYS.XMLTYPE 类型自定义处理器 * * User: liyd * Date: 13-12-27 * Time: 下午4:53 */ public class XmltypeTypeHandler implements TypeHandler<String> { @Override public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { } ... }
这个setParameter方法就是Mybatis在把数据插入到数据库时用来设置参数的,至于这个方法的参数相信你看代码也已经明白了,我们按照前面jdbc的实现方式,在这里插入如下代码:
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { XMLType xmltype = XMLType.createXML(ps.getConnection(), parameter); ps.setObject(i,xmltype); }
并在mapper-config.xml中注册转换器,因为Mybatis定义的枚举org.apache.ibatis.type.JdbcType中,没有我们需要的XMLType类型,在这里我们定义为UNDEFINED:
<configuration> <typeHandlers> <typeHandler javaType="string" jdbcType="UNDEFINED" handler="com.tyyd.dw.context.XmltypeTypeHandler"/> </typeHandlers> </configuration>
在配置文件参数中,使用我们的定义的转换器,这样Mybatis就能找到了:
#{xmlFile,jdbcType=UNDEFINED},
当然你也可以更规范一点,完整的写出它的类型和使用的转换器:
#{xmlFile,javaType=string,jdbcType=UNDEFINED,typeHandler=com.tyyd.dw.context.XmltypeTypeHandler},
完成上面的步骤,照理说一切都大功告成了,我们来运行一下。
结果抛出了异常:java.lang.ClassCastException: org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to oracle.jdbc.OracleConnection
不能转换为Oracle的连接对象OracleConnection,查看一下,发现我们数据源使用的是apache的dbcp,应该是两者不兼容吧。网上查了一下,有位仁兄说是给了个完美解决文案,就是在setParameter方法内再独自加载一个Oracle的驱动类来创建一个connection,如下:
Class.forName("oracle.jdbc.OracleDriver"); Connection connection = DriverManager.getConnection(url, username, password);
这个确实能100%解决连接对象不能转换的问题,但是实现方式上,呵呵,还是不做评论了。还有网上在传来传去的,说是可以转换成PoolableConnection 对象,再使用getDelegate方法可以获得原始代理链接,这个貌似可行,我们来试试:
PoolableConnection connection = (PoolableConnection )ps.getConnection(); XMLType xmltype = XMLType.createXML(connection.getDelegate(), parameter); ps.setObject(i,xmltype);
结果又抛出了异常:
org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to org.apache.commons.dbcp.PoolableConnection,不能转换。
没办法,看来网上传来传去的文章不怎么可靠,没捷径了还是自己看看源代码吧。
通过查看源代码,我们发现PoolableConnection继承了DelegatingConnection类,而DelegatingConnection类实现了Connection接口,我们把它转换成DelegatingConnection试试:
DelegatingConnection connection = (DelegatingConnection )ps.getConnection(); XMLType xmltype = XMLType.createXML(connection.getDelegate(), parameter); ps.setObject(i,xmltype);
结果又抛出异常:无法构造描述符: Invalid arguments; nested exception is java.sql.SQLException: 无法构造描述符: Invalid arguments,通过断点调试,发现connection对象居然是null,怎么会是null呢,网上人家都用的好好的,到我这里就都不行了,真是蛋疼,这不会无解吧,难道真要像上面那位仁兄说的独自加载一个驱动类?没办法,再研究研究吧。
最后发现,通过getMetaData方法可以获取它的原始代理连接,柳暗花明啊,赶紧写上测试,终于正常了,不容易啊,最终代码如下:
@Override public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { DelegatingConnection connection = (DelegatingConnection) ps.getConnection().getMetaData() .getConnection(); XMLType xmltype = XMLType.createXML(connection.getDelegate(), parameter); ps.setObject(i, xmltype); }
至此,使用Mybatis操作XMLType类型终于是搞定了,过程是一波三折啊。数据有插入当然要有查询,接下来就要实现XMLType类型的查询操作了。