欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java使用JDBC或MyBatis框架向Oracle中插入XMLType數(shù)據(jù)

 更新時(shí)間:2016年07月04日 14:47:02   作者:碼農(nóng)的士首席的哥隊(duì)長(zhǎng)  
XMLType是Oracle支持的一種基于XML格式存儲(chǔ)的數(shù)據(jù)類型,這里我們共同來(lái)探究Java使用JDBC或MyBatis框架向Oracle中插入XMLType數(shù)據(jù)的方法:

先來(lái)了解一下什么是XMLType類型。
XMLType是Oracle從9i開(kāi)始特有的數(shù)據(jù)類型,是一個(gè)繼承了Blob的強(qiáng)大存在,可以用來(lái)存儲(chǔ)xml并提供了相當(dāng)多的操作函數(shù)。理論上可以保存2G大小的數(shù)據(jù)。
那怎么樣通過(guò)java來(lái)插入XMLType類型的數(shù)據(jù)呢?項(xiàng)目當(dāng)中采用的是Mybatis,總是出現(xiàn)莫名的異常,都搞不清楚到底是Mybatis的問(wèn)題還是jdbc本身的問(wèn)題,所以打算一步步來(lái),先搞定jdbc,再解決Mybatis。

JDBC
在折騰了半天之后,發(fā)現(xiàn)jdbc操作主要有3種方法:
一、在Java中把XMLType當(dāng)作字符串String來(lái)用,具體創(chuàng)建XMLType的任務(wù)完全交給數(shù)據(jù)庫(kù):

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();

此方法會(huì)使數(shù)據(jù)庫(kù)的壓力偏大,因?yàn)榇朔椒ê?jiǎn)單又不需要額外的依賴,在一開(kāi)始采用此方法,但在實(shí)際使用過(guò)程中發(fā)現(xiàn),在內(nèi)容的長(zhǎng)度超過(guò)4000左右的時(shí)候,會(huì)拋出:ORA-01461: can bind a LONG value only for insert into a LONG column 異常。一開(kāi)始以為使用mybatis的原因,使用jdbc測(cè)試依然如此,使用諸多方法嘗試無(wú)解。在項(xiàng)目中使用該大字段不可能只保存長(zhǎng)度在4000以內(nèi)的數(shù)據(jù),這樣使用varchar2足矣,所以該方法淘汰。

二、使用CLOB類型來(lái)操作。XMLType是繼承了CLOB的存在,所以是可以通過(guò)CLOB來(lái)操作的。方法是在客戶端創(chuàng)建好CLOB數(shù)據(jù)后傳入數(shù)據(jù)庫(kù)通過(guò)Oracle的XMLTYPE()函數(shù)來(lái)構(gòu)造XMLType的值:

String sql = "insert into xmltable (XML) values(XMLType(?))"; 
String xmldata = "<label>This is an XML fragment</label>"; 
//通過(guò)conn創(chuàng)建CLOB
CLOB tempClob = CLOB.createTemporary(connection, false, CLOB.DURATION_SESSION);
//打開(kāi)CLOB
tempClob.open(CLOB.MODE_READWRITE);
//獲得writer
Writer clobWriter = tempClob.setCharacterStream(100);
//寫(xiě)入數(shù)據(jù)
clobWriter.write(xmldata);
//刷新
clobWriter.flush();
//關(guān)閉writer
clobWriter.close();
//關(guān)閉CLOB
tempClob.close();
pst.setObject(1, tempClob);

此方法客戶端和數(shù)據(jù)庫(kù)同時(shí)承擔(dān)了創(chuàng)建XMLType的任務(wù),因此壓力較平均,也沒(méi)有超過(guò)長(zhǎng)度的問(wèn)題。但是在實(shí)際使用過(guò)程中又發(fā)現(xiàn),xml的內(nèi)容頭部不能包含以下信息:

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

否則會(huì)拋出異常:

PI names starting with XML are reserved

先不說(shuō)少了這個(gè)在以后處理xml內(nèi)容包含中文時(shí)會(huì)不會(huì)遇到蛋疼的亂碼問(wèn)題,光是看著就讓人感覺(jué)不爽,且需求上也要求保存,沒(méi)辦法,這個(gè)方法又行不通了。

三、使用Oracle提供的oracle.xdb.XMLType類,客戶端創(chuàng)建XMLType后直接把對(duì)象傳給數(shù)據(jù)庫(kù):

Connection conn = ... ;//獲得Connection 
PreparedStatement ps = ...;//獲得PreparedSatement 
String sql = "insert into xmltable (XML) values(?)"; 
String xmldata = "<label>This is an XML fragment</label>"; 
//創(chuàng)建一個(gè)XMLType對(duì)象 
XMLType xmltype = XMLType.createXML(conn, xmldata); 
ps.setObject(1, xmltype); 
ps.executeUpdate();

此方法將創(chuàng)建XMLType的任務(wù)完全交給了客戶端,因此客戶端的壓力大,數(shù)據(jù)庫(kù)壓力小。在實(shí)測(cè)過(guò)程中,需要添加兩個(gè)jar包,不然會(huì)報(bào)找不到類的錯(cuò)誤:

xdb.jar
xmlparserv2.jar

需要注意這jar包又沒(méi)版本標(biāo)注,很容易弄錯(cuò),一開(kāi)始我下載了個(gè)xdb.jar,怎么弄都不對(duì)提示找不到某個(gè)類,查看之后發(fā)現(xiàn)是屬于oracle更早期版本,重新下載了一個(gè)xdb.jar后正常。
以上三種方法通過(guò)插入20萬(wàn)條數(shù)據(jù)測(cè)試比較發(fā)現(xiàn):

第一種方法:耗時(shí)最短,服務(wù)器cpu消耗最大;
第二種方法:耗時(shí)最長(zhǎng),服務(wù)器cpu消耗居中;
第三種方法:耗時(shí)居中,服務(wù)器cpu消耗最小.
 
至此,jdbc操作XMLType類型數(shù)據(jù)終于算是小小的搞定了,不用說(shuō)采用了第三種方案,但是項(xiàng)目中基本都不會(huì)直接用jdbc來(lái)操作,像當(dāng)前項(xiàng)目中就采用了Mybatis,上面也講到了使用Mybatis總是出現(xiàn)異常,查看了下Mybatis也沒(méi)有對(duì)XMLType的實(shí)現(xiàn),看來(lái)還有的折騰,不過(guò)jdbc已經(jīng)搞定,思路已經(jīng)清晰了不是?


Mybatis
使用Mybatis操作XMLType,我們同樣在Java端映射為String類型,當(dāng)直接操作不做任何處理時(shí),和jdbc大體一樣,傳輸?shù)膬?nèi)容長(zhǎng)度小于4000時(shí)一切正常,當(dāng)傳輸?shù)膬?nèi)容長(zhǎng)度超過(guò)4000左右時(shí),同樣拋出異常:

ORA-01461: can bind a LONG value only for insert into a LONG column

可見(jiàn),Mybatis的操作其實(shí)和jdbc是一樣的,只不過(guò)它在jdbc的外面又封裝了一層,使得我們可以采用配置文件等映射的方式來(lái)更方便的訪問(wèn)數(shù)據(jù)庫(kù),我們要做的,就是在原有Mybatis便捷性的基礎(chǔ)上實(shí)現(xiàn)對(duì)XMLType類型數(shù)據(jù)的插入,這種情況下,實(shí)現(xiàn)一個(gè)XMLType類型的自定義TypeHandler處理器是最好的選擇。

這里,我們?nèi)匀徊捎们懊嫣岬降姆桨溉?,自然那兩個(gè)jar包:xdb.jar,xmlparserv2.jar也是要加入的。

添加一個(gè)XmltypeTypeHandler,實(shí)現(xiàn)TypeHandler接口,由于插入數(shù)據(jù)主要用到setParameter方法,所以這里只列出該方法,其它方法代碼略:

/**
 * oracle SYS.XMLTYPE 類型自定義處理器
 */
public class XmltypeTypeHandler implements TypeHandler<String> {
 @Override
 public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
 }
 ...
}

這個(gè)setParameter方法就是Mybatis在把數(shù)據(jù)插入到數(shù)據(jù)庫(kù)時(shí)用來(lái)設(shè)置參數(shù)的,至于這個(gè)方法的參數(shù)相信你看代碼也已經(jīng)明白了,我們按照前面jdbc的實(shí)現(xiàn)方式,在這里插入如下代碼:

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中注冊(cè)轉(zhuǎn)換器,因?yàn)镸ybatis定義的枚舉org.apache.ibatis.type.JdbcType中,沒(méi)有我們需要的XMLType類型,在這里我們定義為UNDEFINED:

<configuration>
 <typeHandlers>
  <typeHandler javaType="string" jdbcType="UNDEFINED" handler="com.tyyd.dw.context.XmltypeTypeHandler"/>
 </typeHandlers>
</configuration>

在配置文件參數(shù)中,使用我們的定義的轉(zhuǎn)換器,這樣Mybatis就能找到了:

#{xmlFile,jdbcType=UNDEFINED},

當(dāng)然你也可以更規(guī)范一點(diǎn),完整的寫(xiě)出它的類型和使用的轉(zhuǎn)換器:

#{xmlFile,javaType=string,jdbcType=UNDEFINED,typeHandler=com.tyyd.dw.context.XmltypeTypeHandler},
完成上面的步驟,照理說(shuō)一切都大功告成了,我們來(lái)運(yùn)行一下。

結(jié)果拋出了異常:java.lang.ClassCastException: org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to oracle.jdbc.OracleConnection

不能轉(zhuǎn)換為Oracle的連接對(duì)象OracleConnection,查看一下,發(fā)現(xiàn)我們數(shù)據(jù)源使用的是apache的dbcp,應(yīng)該是兩者不兼容吧。網(wǎng)上查了一下,有位仁兄說(shuō)是給了個(gè)完美解決文案,就是在setParameter方法內(nèi)再獨(dú)自加載一個(gè)Oracle的驅(qū)動(dòng)類來(lái)創(chuàng)建一個(gè)connection,如下:

Class.forName("oracle.jdbc.OracleDriver");
Connection connection = DriverManager.getConnection(url, username, password);

這個(gè)確實(shí)能100%解決連接對(duì)象不能轉(zhuǎn)換的問(wèn)題,但是實(shí)現(xiàn)方式上,呵呵,還是不做評(píng)論了。還有網(wǎng)上在傳來(lái)傳去的,說(shuō)是可以轉(zhuǎn)換成PoolableConnection 對(duì)象,再使用getDelegate方法可以獲得原始代理鏈接,這個(gè)貌似可行,我們來(lái)試試:

PoolableConnection connection = (PoolableConnection )ps.getConnection();
XMLType xmltype = XMLType.createXML(connection.getDelegate(), parameter);
ps.setObject(i,xmltype);

結(jié)果又拋出了異常:

org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to org.apache.commons.dbcp.PoolableConnection,不能轉(zhuǎn)換。

沒(méi)辦法,看來(lái)網(wǎng)上傳來(lái)傳去的文章不怎么可靠,沒(méi)捷徑了還是自己看看源代碼吧。

通過(guò)查看源代碼,我們發(fā)現(xiàn)PoolableConnection繼承了DelegatingConnection類,而DelegatingConnection類實(shí)現(xiàn)了Connection接口,我們把它轉(zhuǎn)換成DelegatingConnection試試:

DelegatingConnection connection = (DelegatingConnection )ps.getConnection();
XMLType xmltype = XMLType.createXML(connection.getDelegate(), parameter);
ps.setObject(i,xmltype);

結(jié)果又拋出異常:無(wú)法構(gòu)造描述符: Invalid arguments; nested exception is java.sql.SQLException: 無(wú)法構(gòu)造描述符: Invalid arguments,通過(guò)斷點(diǎn)調(diào)試,發(fā)現(xiàn)connection對(duì)象居然是null,怎么會(huì)是null呢,網(wǎng)上人家都用的好好的,到我這里就都不行了,真是蛋疼,這不會(huì)無(wú)解吧,難道真要像上面那位仁兄說(shuō)的獨(dú)自加載一個(gè)驅(qū)動(dòng)類?沒(méi)辦法,再研究研究吧。

最后發(fā)現(xiàn),通過(guò)getMetaData方法可以獲取它的原始代理連接,柳暗花明啊,趕緊寫(xiě)上測(cè)試,終于正常了,不容易啊,最終代碼如下:

@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類型終于是搞定了,過(guò)程是一波三折啊。數(shù)據(jù)有插入當(dāng)然要有查詢,接下來(lái)就要實(shí)現(xiàn)XMLType類型的查詢操作了。

相關(guān)文章

  • 淺談java中異步多線程超時(shí)導(dǎo)致的服務(wù)異常

    淺談java中異步多線程超時(shí)導(dǎo)致的服務(wù)異常

    下面小編就為大家?guī)?lái)一篇淺談java中異步多線程超時(shí)導(dǎo)致的服務(wù)異常。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-06-06
  • Java實(shí)現(xiàn)優(yōu)雅的參數(shù)校驗(yàn)方法詳解

    Java實(shí)現(xiàn)優(yōu)雅的參數(shù)校驗(yàn)方法詳解

    這篇文章主要為大家詳細(xì)介紹了Java語(yǔ)言如何實(shí)現(xiàn)優(yōu)雅的參數(shù)校驗(yàn),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定是幫助,需要的可以參考一下
    2022-06-06
  • Mybatis中xml的動(dòng)態(tài)sql實(shí)現(xiàn)示例

    Mybatis中xml的動(dòng)態(tài)sql實(shí)現(xiàn)示例

    本文主要介紹了Mybatis中xml的動(dòng)態(tài)sql實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Java輕松使用工具類實(shí)現(xiàn)獲取wav時(shí)間長(zhǎng)度

    Java輕松使用工具類實(shí)現(xiàn)獲取wav時(shí)間長(zhǎng)度

    在Java中,工具類定義了一組公共方法,這篇文章將介紹Java中使用工具類來(lái)獲取一個(gè)wav文件的時(shí)間長(zhǎng)度,感興趣的同學(xué)繼續(xù)往下閱讀吧
    2021-10-10
  • Java 中利用泛型和反射機(jī)制抽象DAO的實(shí)例

    Java 中利用泛型和反射機(jī)制抽象DAO的實(shí)例

    這篇文章主要介紹了Java 中利用泛型和反射機(jī)制抽象DAO的實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • 淺談PrintStream和PrintWriter的區(qū)別和聯(lián)系

    淺談PrintStream和PrintWriter的區(qū)別和聯(lián)系

    這篇文章主要介紹了淺談PrintStream和PrintWriter的區(qū)別和聯(lián)系,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • SpringBoot中的HATEOAS詳情

    SpringBoot中的HATEOAS詳情

    這篇文章主要介紹了SpringBoot中的HATEOAS詳情,SpringBoot提供了HATEOAS的便捷使用方式,文章圍繞主題展開(kāi)詳細(xì)介紹內(nèi)容,需要的小伙伴可以參考一下
    2022-05-05
  • 使用Java實(shí)現(xiàn)驗(yàn)證碼程序

    使用Java實(shí)現(xiàn)驗(yàn)證碼程序

    這篇文章主要為大家詳細(xì)介紹了使用Java實(shí)現(xiàn)驗(yàn)證碼程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Java正確使用訪問(wèn)修飾符的姿勢(shì)

    Java正確使用訪問(wèn)修飾符的姿勢(shì)

    訪問(wèn)修飾符是Java語(yǔ)法中很基礎(chǔ)的一部分,但是能正確的使用Java訪問(wèn)修飾符的程序員只在少數(shù),下面這篇文章主要給大家介紹了關(guān)于Java正確使用訪問(wèn)修飾符的姿勢(shì),需要的朋友可以參考下
    2021-11-11
  • 解析Java按值傳遞還是按引用傳遞

    解析Java按值傳遞還是按引用傳遞

    這篇文章主要介紹了解析Java按值傳遞還是按引用傳遞,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01

最新評(píng)論