一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專(zhuān)注于服務(wù)器技術(shù)及軟件下載分享
分類(lèi)導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務(wù)器之家 - 編程語(yǔ)言 - JAVA教程 - Java使用JDBC或MyBatis框架向Oracle中插入XMLType數(shù)據(jù)

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

2020-05-24 12:08碼農(nóng)的士首席的哥隊(duì)長(zhǎng) JAVA教程

XMLType是Oracle支持的一種基于XML格式存儲(chǔ)的數(shù)據(jù)類(lèi)型,這里我們共同來(lái)探究Java使用JDBC或MyBatis框架向Oracle中插入XMLType數(shù)據(jù)的方法:

先來(lái)了解一下什么是XMLType類(lèi)型。
XMLType是Oracle從9i開(kāi)始特有的數(shù)據(jù)類(lèi)型,是一個(gè)繼承了Blob的強(qiáng)大存在,可以用來(lái)存儲(chǔ)xml并提供了相當(dāng)多的操作函數(shù)。理論上可以保存2G大小的數(shù)據(jù)。
那怎么樣通過(guò)java來(lái)插入XMLType類(lèi)型的數(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ù):

?
1
2
3
4
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)型來(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的值:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)容頭部不能包含以下信息:

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

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

?
1
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類(lèi),客戶端創(chuàng)建XMLType后直接把對(duì)象傳給數(shù)據(jù)庫(kù):

?
1
2
3
4
5
6
7
8
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)找不到類(lèi)的錯(cuò)誤:

?
1
2
xdb.jar
xmlparserv2.jar

需要注意這jar包又沒(méi)版本標(biāo)注,很容易弄錯(cuò),一開(kāi)始我下載了個(gè)xdb.jar,怎么弄都不對(duì)提示找不到某個(gè)類(lèi),查看之后發(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類(lèi)型數(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類(lèi)型,當(dāng)直接操作不做任何處理時(shí),和jdbc大體一樣,傳輸?shù)膬?nèi)容長(zhǎng)度小于4000時(shí)一切正常,當(dāng)傳輸?shù)膬?nèi)容長(zhǎng)度超過(guò)4000左右時(shí),同樣拋出異常:

?
1
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類(lèi)型數(shù)據(jù)的插入,這種情況下,實(shí)現(xiàn)一個(gè)XMLType類(lèi)型的自定義TypeHandler處理器是最好的選擇。

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

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

?
1
2
3
4
5
6
7
8
9
/**
 * oracle SYS.XMLTYPE 類(lèi)型自定義處理器
 */
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)方式,在這里插入如下代碼:

?
1
2
3
4
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類(lèi)型,在這里我們定義為UNDEFINED:

?
1
2
3
4
5
<configuration>
 <typeHandlers>
  <typeHandler javaType="string" jdbcType="UNDEFINED" handler="com.tyyd.dw.context.XmltypeTypeHandler"/>
 </typeHandlers>
</configuration>

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

?
1
#{xmlFile,jdbcType=UNDEFINED},

當(dāng)然你也可以更規(guī)范一點(diǎn),完整的寫(xiě)出它的類(lèi)型和使用的轉(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)來(lái)創(chuàng)建一個(gè)connection,如下:

?
1
2
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)試試:

?
1
2
3
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類(lèi),而DelegatingConnection類(lèi)實(shí)現(xiàn)了Connection接口,我們把它轉(zhuǎn)換成DelegatingConnection試試:

?
1
2
3
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)類(lèi)?沒(méi)辦法,再研究研究吧。

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 好大好爽好硬我要喷水了 | 免费的伦理片 | 日本人作爰啪啪全过程 | 国产美女亚洲精品久久久综合91 | 美女扒开屁股让我桶免费 | 四虎国产精品视频免费看 | 天天干夜夜玩 | 国产99区| 国语第一次处破女 | 亚洲精品成人456在线播放 | 婷婷色在线 | 国产精品免费aⅴ片在线观看 | tobu8在线观看免费高清 | 性做久久久久久久久浪潮 | 国产成人免费片在线视频观看 | 久久中文字幕无线观看 | 四虎永久在线精品国产 | 久久国产主播福利在线 | 我的妹妹最近有点怪免费播放 | 日本人啪啪 | 97自拍视频在线观看 | 91色+91sesex| 国产女主播在线播放一区二区 | 婷婷99视频精品全部在线观看 | 羞羞麻豆国产精品1区2区3区 | 欧美视频黑鬼大战白妞 | 小浪妇奶真大水多 | 日韩高清一区二区 | 青草国产福利视频免费观看 | 免费一级毛片在线播放放视频 | 暖暖 免费 高清 日本 中文 | 高清在线观看mv的网址免费 | 免费一级国产生活片 | 青青国产在线视频 | chinese男性厕所撒尿合集 | 国产网站视频 | 人阁色第四影院在线观看 | 啪啪免费网址 | 久久综合视频网站 | 91精品大神国产在线播放 | 女人爽到喷水的视频免费看 |