Java使用JDBC或MyBatis框架向Oracle中插入XMLType数据

先来了解一下什么是XMLType类型。
XMLType是Oracle从9i开始特有的数据类型,是一个继承了Blob的强大存在,可以用来存储xml并提供了相当多的操作函数。理论上可以保存2G大小的数据。
那怎么样通过java来插入XMLType类型的数据呢?项目当中采用的是Mybatis,总是出现莫名的异常,都搞不清楚到底是Mybatis的问题还是jdbc本身的问题,所以打算一步步来,先搞定jdbc,再解决Mybatis。

JDBC
在折腾了半天之后,发现jdbc操作主要有3种方法:
一、在Java中把XMLType当作字符串String来用,具体创建XMLType的任务完全交给数据库:

String sql = "insert into xmltable (XML) values(sys.xmlType.createXML(?))";
String xmldata = "<label>This is an XML fragment</label>";
ps.setString(1, xmldata);
ps.executeUpdate();

此方法会使数据库的压力偏大,因为此方法简单又不需要额外的依赖,在一开始采用此方法,但在实际使用过程中发现,在内容的长度超过4000左右的时候,会抛出:ORA-01461: can bind a LONG value only for insert into a LONG column 异常。一开始以为使用mybatis的原因,使用jdbc测试依然如此,使用诸多方法尝试无解。在项目中使用该大字段不可能只保存长度在4000以内的数据,这样使用varchar2足矣,所以该方法淘汰。

二、使用CLOB类型来操作。XMLType是继承了CLOB的存在,所以是可以通过CLOB来操作的。方法是在客户端创建好CLOB数据后传入数据库通过Oracle的XMLTYPE()函数来构造XMLType的值:

String sql = "insert into xmltable (XML) values(XMLType(?))";
String xmldata = "<label>This is an XML fragment</label>";
//通过conn创建CLOB
CLOB tempClob = CLOB.createTemporary(connection, false, CLOB.DURATION_SESSION);
//打开CLOB
tempClob.open(CLOB.MODE_READWRITE);
//获得writer
Writer clobWriter = tempClob.setCharacterStream(100);
//写入数据
clobWriter.write(xmldata);
//刷新
clobWriter.flush();
//关闭writer
clobWriter.close();
//关闭CLOB
tempClob.close();
pst.setObject(1, tempClob);

此方法客户端和数据库同时承担了创建XMLType的任务,因此压力较平均,也没有超过长度的问题。但是在实际使用过程中又发现,xml的内容头部不能包含以下信息:

<?xml version="1.0" encoding="UTF-8"?>

否则会抛出异常:

PI names starting with XML are reserved

先不说少了这个在以后处理xml内容包含中文时会不会遇到蛋疼的乱码问题,光是看着就让人感觉不爽,且需求上也要求保存,没办法,这个方法又行不通了。

三、使用Oracle提供的oracle.xdb.XMLType类,客户端创建XMLType后直接把对象传给数据库:

Connection conn = ... ;//获得Connection
PreparedStatement ps = ...;//获得PreparedSatement
String sql = "insert into xmltable (XML) values(?)";
String xmldata = "<label>This is an XML fragment</label>";
//创建一个XMLType对象
XMLType xmltype = XMLType.createXML(conn, xmldata);
ps.setObject(1, xmltype);
ps.executeUpdate();

此方法将创建XMLType的任务完全交给了客户端,因此客户端的压力大,数据库压力小。在实测过程中,需要添加两个jar包,不然会报找不到类的错误:

xdb.jar
xmlparserv2.jar

需要注意这jar包又没版本标注,很容易弄错,一开始我下载了个xdb.jar,怎么弄都不对提示找不到某个类,查看之后发现是属于oracle更早期版本,重新下载了一个xdb.jar后正常。
以上三种方法通过插入20万条数据测试比较发现:

第一种方法:耗时最短,服务器cpu消耗最大;
第二种方法:耗时最长,服务器cpu消耗居中;
第三种方法:耗时居中,服务器cpu消耗最小.
 
至此,jdbc操作XMLType类型数据终于算是小小的搞定了,不用说采用了第三种方案,但是项目中基本都不会直接用jdbc来操作,像当前项目中就采用了Mybatis,上面也讲到了使用Mybatis总是出现异常,查看了下Mybatis也没有对XMLType的实现,看来还有的折腾,不过jdbc已经搞定,思路已经清晰了不是?

Mybatis
使用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处理器是最好的选择。

这里,我们仍然采用前面提到的方案三,自然那两个jar包:xdb.jar,xmlparserv2.jar也是要加入的。

添加一个XmltypeTypeHandler,实现TypeHandler接口,由于插入数据主要用到setParameter方法,所以这里只列出该方法,其它方法代码略:

/**
 * oracle SYS.XMLTYPE 类型自定义处理器
 */
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类型的查询操作了。

(0)

相关推荐

  • Java使用JDBC实现Oracle用户认证的方法详解

    本文实例讲述了Java使用JDBC实现Oracle用户认证的方法.分享给大家供大家参考,具体如下: 两天时间写的小品,以前的J2EE环境基本使用框架.现在使用JDBC配合Oracle存储过程模拟了一下用户注册和用户认证. 一.添加必须的jar包 需要JDBC连接Oracle的包和shiro-core依赖,添加shiro-core主要为了方便使用SHA-256散列算法. 二.编写JDBC连接 import java.sql.Connection; import java.sql.DriverMan

  • java使用jdbc链接Oracle示例类分享

    复制代码 代码如下: public class OracleJdbcTest   {       String driverClass = "oracle.jdbc.driver.OracleDriver"; Connection con; public void init(FileInputStream fs) throws ClassNotFoundException, SQLException, FileNotFoundException, IOException       {

  • Java使用JDBC连接Oracle_MSSQL实例代码

    一.Statement 复制代码 代码如下: import java.sql.*; public class TestJDBC { public static void main(String[] args) {  Connection oracle_conn = null;  Statement oracle_stmt = null;  ResultSet oracle_rs = null; Connection mssql_conn = null;  Statement mssql_stmt

  • 如何在Eclipse中设置Oracle的JDBC

    第一步:找到jdbc驱动我用的oracle自带的,ORACLE_HOME]\jdbc\lib\ 下面有各种jdbc,一定要注意兼容问题,具体看第二步,分别用哪个文件. 第二步:把驱动的路径加在环境变量classpath内- Add [ORACLE_HOME]\jdbc\lib\classes111.zip and[ORACLE_HOME]\jdbc\lib\nls_charset11.zip to your CLASSPATH.(Add classes12.zip and nls_charset

  • 使用JDBC4.0操作Oracle中BLOB类型的数据方法

    在JDBC4.0推出后,它的从多的特性正在受到广泛地关注.下面通过本文给大家介绍JDBC4.0操作Oracle中BLOB类型的数据的方法. 需要的jar包 使用ojdbc6.jar 在/META-INF/MANIFEST.MF里可以看到Specification-Version: 4.0 建表 create sequence seq_blobmodel_id start with 1 increment by 1 nocache; create table blobmodel ( blobid

  • Java与Oracle实现事务(JDBC事务)实例详解

    Java与Oracle实现事务(JDBC事务)实例详解 J2EE支持JDBC事务.JTA事务和容器事务事务,这里说一下怎样实现JDBC事务. JDBC事务是由Connection对象所控制的,它提供了两种事务模式:自己主动提交和手动提交,默认是自己主动提交. 自己主动提交就是:在JDBC中.在一个连接对象Connection中.默认把每一个SQL语句的运行都当做是一个事务(即每次运行完SQL语句都会马上将操作更新到数据库). 手动提交就是:当须要一次性运行多个SQL语句,将多个SQL语句组成一个

  • 解析jdbc处理oracle的clob字段的详解

    import java.io.BufferedOutputStream;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.Reader;import java.sql.Clob;import java.sql.Connection;import java.sql.ResultSet;

  • Java开发Oracle数据库连接JDBC Thin Driver 的三种方法

    Oracle的jdbc驱动三种主要分类: 1.JDBC OCI: oci是oracle call interface的缩写,此驱动类似于传统的ODBC 驱动.因为它需要Oracle Call Interface and Net8,所以它需要在运行使用此驱动的JAVA程序的机器上安装客户端软件,其实主要是用到orcale客户端里以dll方式提供的oci和服务器配置. 2.JDBC Thin: thin是for thin client的意思,这种驱动一般用在运行在WEB浏览器中的JAVA程序.它不是

  • java基于JDBC连接Oracle 11g Release2实例分析

    本文实例讲述了java基于JDBC连接Oracle 11g Release2的方法.分享给大家供大家参考.具体如下: Oracle 11g Release 2 的 JDBC 连接似乎有所不同 ,如果你收到下面的异常: Listener refused the connection with the following error:ORA-12505, TNS:listener does not currently know of SID given in connect descriptor.

  • JDBC连接Oracle数据库常见问题及解决方法

    注:本文是斑竹从JDBC & Transaction版摘录广大站友的提问以及各种解答整理而来,如果您认为本文的内容已触犯了您的权益,请联系管理员进行修改. Jbuilder正确连接 oracle 9i需要注意的几个问题 oracle8以上的应该都使用classes12.jar文件作为jdbc驱动: 正确设置windows的classpath和jbuilder中的enterprise setup .configure libraries,将casses12.jar路径正确添加到上述需要设置的地方:

随机推荐