日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring JDBC-使用Spring JDBC获取本地连接对象以及操作BLOB/CLOB类型数据

發(fā)布時間:2025/3/21 javascript 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring JDBC-使用Spring JDBC获取本地连接对象以及操作BLOB/CLOB类型数据 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

  • 概述
  • 如何獲取本地?cái)?shù)據(jù)連接
    • 示例從DBCP數(shù)據(jù)源中獲取Oracle的本地連接對象
  • 相關(guān)接口操作
    • LobCreator
    • LobHandler
  • 插入LOB類型的數(shù)據(jù)
  • 以塊數(shù)據(jù)的方式讀取LOB數(shù)據(jù)
  • 以流數(shù)據(jù)的方式讀取LOB數(shù)據(jù)
  • 示例源碼

概述

我們在Spring-使用Spring JDBC訪問數(shù)據(jù)庫使用JDBC進(jìn)行了CRUD(Create Retrieve Update Delete增刪改查)以及調(diào)用存過的操作,這里我們將進(jìn)一步了解一些高級的數(shù)據(jù)庫操作知識,包括獲取本地?cái)?shù)據(jù)連接進(jìn)行數(shù)據(jù)庫相關(guān)的操作和如何操作BLOB、CLBO這些LOB數(shù)據(jù)。

LOB 代表大對象數(shù)據(jù),包括 BLOB 和 CLOB 兩種類型。

  • BLOB 用于存儲大塊的二進(jìn)制數(shù)據(jù),如圖片數(shù)據(jù),視頻數(shù)據(jù)等(議案不宜將文件存儲到數(shù)據(jù)中,而應(yīng)該存儲到專門的文件服務(wù)器中)
  • CLOB 用于存儲長文本數(shù)據(jù),如產(chǎn)品的詳細(xì)描述等。

值得注意的是:在不同的數(shù)據(jù)庫中,大對象對應(yīng)的字段類型是不盡相同的,如 DB2 對應(yīng) BLOB/CLOB,MySql 對應(yīng) BLOB/LONGTEXT,SqlServer 對應(yīng) IMAGE/TEXT。

需要指出的是,有些數(shù)據(jù)庫的大對象類型可以象簡單類型一樣訪問,如 MySql 的 LONGTEXT 的操作方式和 VARCHAR 類型一樣。在一般情況下, LOB 類型數(shù)據(jù)的訪問方式不同于其它簡單類型的數(shù)據(jù),我們經(jīng)常會以流的方式操作 LOB 類型的數(shù)據(jù)。

此外,LOB 類型數(shù)據(jù)的訪問不是線程安全的,需要為其單獨(dú)分配相應(yīng)的數(shù)據(jù)庫資源,并在操作完成后釋放資源。

Spring 在 org.springframework.jdbc.support.lob 包中為我們提供了相應(yīng)的幫助類,以便解決上述疑難雜癥。

Spring 大大降低了我們處理 LOB 數(shù)據(jù)的難度。

  • 首先,Spring 提供了 NativeJdbcExtractor 接口,我們可以在不同環(huán)境里選擇相應(yīng)的實(shí)現(xiàn)類從數(shù)據(jù)源中獲取本地 JDBC對象;

  • 其次,Spring 通過 LobCreator 接口取消了不同數(shù)據(jù)廠商操作 LOB 數(shù)據(jù)的差別,并提供了創(chuàng)建 LobCreator 和LobHandler 接口,我們只要根據(jù)底層數(shù)據(jù)庫類型選擇合適的 LobHandler 進(jìn)行配置即可。


如何獲取本地?cái)?shù)據(jù)連接

我們知道,在 Web 應(yīng)用服務(wù)器或 Spring 中配置數(shù)據(jù)源時,從數(shù)據(jù)源中返回的數(shù)據(jù)連接對象是本地 JDBC 對象(如 DB2Connection、OracleConnection)的代理類,這是因?yàn)閿?shù)據(jù)源需要改變數(shù)據(jù)連接原有的行為以便施加額外的控制,比如在調(diào)用Connection#close()方法時,將數(shù)據(jù)連接返還到連接池中而非將其關(guān)閉。

但是我們在某些情況下,希望得到被代理前的本地JDBC對象,比如OracleConnection或者OracleResultSet,以便調(diào)用這些驅(qū)動程序廠商相關(guān)的API完成一些特殊的操作。

為了獲取本地JDBC對象,Spring在org.framework.jdbc.support.nativejdbc包下定義了NativeJdbcExtractor接口并提供了實(shí)現(xiàn)類。 NativeJdbcExtractor接口定義了從數(shù)據(jù)源JDBC對象抽取本地JDBC對象的方法。

我們來看下NativeJdbcExtractor接口幾個重要的方法

  • Connection getNativeConnection(Connection con) 獲取本地 Connection 對象
  • Connection getNativeConnectionFromStatement(Statement stmt)獲取本地 Statement 對象
  • PreparedStatement getNativePreparedStatement(PreparedStatement ps)獲取本地 PreparedStatement 對象
  • ResultSet getNativeResultSet(ResultSet rs)獲取本地 ResultSet 對象
  • CallableStatement getNativeCallableStatement(CallableStatement cs) 獲取本地 CallableStatement 對象

有些簡單的數(shù)據(jù)源僅對 Connection 對象進(jìn)行代理,這時可以直接使用 SimpleNativeJdbcExtractor 實(shí)現(xiàn)類。但有些數(shù)據(jù)源(如 Jakarta Commons DBCP)會對所有的 JDBC 對象進(jìn)行代理,這時,就需要根據(jù)具體的情況選擇適合的抽取器實(shí)現(xiàn)類了。下表列出了不同數(shù)據(jù)源本地 JDBC 對象抽取器的實(shí)現(xiàn)類:

數(shù)據(jù)源類型本地JDBC對象抽取類
WebSphere 4 及以上版本的數(shù)據(jù)源org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor
WebLogic 6.1+ 及以上版本的數(shù)據(jù)源org.springframework.jdbc.support.nativejdbc.WebLogicNativeJdbcExtractor
JBoss 3.2.4 及以上版本的數(shù)據(jù)源org.springframework.jdbc.support.nativejdbc.JBossNativeJdbcExtractor
C3P0 數(shù)據(jù)源org.springframework.jdbc.support.nativejdbc.C3P0NativeJdbcExtractor
DBCP 數(shù)據(jù)源org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor
ObjectWeb 的 XAPool 數(shù)據(jù)源org.springframework.jdbc.support.nativejdbc.XAPoolNativeJdbcExtractor

示例:從DBCP數(shù)據(jù)源中獲取Oracle的本地連接對象

package com.xgj.dao.lob.nativeConn;import java.sql.Connection; import java.sql.SQLException;import oracle.jdbc.driver.OracleConnection;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.stereotype.Repository;/*** * * @ClassName: ArtisanDaoImpl* * @Description: @Repository標(biāo)注DAO層* * @author: Mr.Yang* * @date: 2017年9月28日 下午5:35:06*/@Repository public class ArtisanDaoImpl {private JdbcTemplate jdbcTemplate;/*** * * @Title: setJdbcTemplate* * @Description: 注入JdbcTemplate* * @param jdbcTemplate* * @return: void*/@Autowiredpublic void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}/*** * * @Title: getNativeConn* * @Description: 要想使這個類正確運(yùn)行,JdbcTemplate模板配置必須調(diào)整,具體見conf-getLocalConnObj.xml* * * @return: void*/public OracleConnection getOracleNativeConn() {OracleConnection oracleConnection = null;try {// 使用DataSourceUtils 從JdbcTemplate中獲取數(shù)據(jù)連接Connection connection = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());// 使用模板類的本地JDBC抽取器獲取本地連接connection = jdbcTemplate.getNativeJdbcExtractor().getNativeConnection(connection);// 強(qiáng)制類型轉(zhuǎn)換oracleConnection = (OracleConnection) connection;// 使用本地對象,調(diào)用API完成業(yè)務(wù)操作(此處省略) 比如使用OracleConnection特殊 API操作lob} catch (CannotGetJdbcConnectionException | SQLException e) {e.printStackTrace();}return oracleConnection;} }

我們通過 DataSourceUtils 獲取當(dāng)前線程綁定的數(shù)據(jù)連接,為了使用線程上下文相關(guān)的事務(wù),通過 DataSourceUtils 從數(shù)據(jù)源中獲取連接是正確的做法,如果直接通過 dateSource 獲取連接,則將得到一個和當(dāng)前線程上下文無關(guān)的數(shù)據(jù)連接實(shí)例。

JdbcTemplate 可以在配置時注入一個本地 JDBC 對象抽取器,要使上述代碼 正確運(yùn)行,我們必須進(jìn)行如下配置:

<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 掃描類包,將標(biāo)注Spring注解的類自動轉(zhuǎn)化Bean,同時完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.lob.nativeConn" /><!-- 不使用context命名空間,則需要定義Bean <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:spring/jdbc.properties" /> </bean> --><!-- 使用context命名空間,同上面的Bean等效.在xml文件中配置數(shù)據(jù)庫的properties文件 --><context:property-placeholder location="classpath:spring/jdbc.properties" /><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close" p:driverClassName="${jdbc.driverClassName}"p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" /><!-- 定義DBCP數(shù)據(jù)源的JDBC本地對象抽取器 --><bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"></bean><!-- 配置Jdbc模板 設(shè)置抽取器 --><bean id="jdbcTemplate" lazy-init="true"class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource"p:nativeJdbcExtractor-ref="nativeJdbcExtractor"/></beans>

單元測試

package com.xgj.dao.lob.nativeConn;import oracle.jdbc.driver.OracleConnection;import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext;public class NativeConnTest {ClassPathXmlApplicationContext ctx = null;@Beforepublic void initContext() {// 啟動Spring 容器ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/dao/lob/nativeConn/conf-getLocalConnObj.xml");System.out.println("initContext successfully");}@Testpublic void testCallProcWithCallableStatementCreator() {ArtisanDaoImpl artisanDaoImpl = ctx.getBean("artisanDaoImpl",ArtisanDaoImpl.class);OracleConnection oracleConnection = artisanDaoImpl.getOracleNativeConn();// 檢查對象不為空Assert.assertNotNull(oracleConnection);}@Afterpublic void closeContext() {if (ctx != null) {ctx.close();}System.out.println("close context successfully");} }

運(yùn)行結(jié)果

單元測試通過,說明oracleConnection不為空,我們獲取到了oracleConnection對象。


相關(guān)接口操作

LobCreator

雖然 JDBC 定義了兩個操作 LOB 類型的接口:java.sql.Blob 和 java.sql.Clob,但有些廠商的 JDBC 驅(qū)動程序并不支持這兩個接口。為此,Spring 定義了一個獨(dú)立于 java.sql.Blob/Clob 的 LobCreator 接口,以統(tǒng)一的方式操作各種數(shù)據(jù)庫的 LOB 類型數(shù)據(jù)。

因?yàn)?LobCreator 本身持有 LOB 所對應(yīng)的數(shù)據(jù)庫資源,所以它不是線程安全的,一個 LobCreator 只能操作一個 LOB 數(shù)據(jù)。

為了方便在 PreparedStatement 中使用 LobCreator,我們可以直接使用如下方法。

JdbcTemplate#execute(String sql,AbstractLobCreatingPreparedStatementCallback lcpsc)

下面對 LobCreator 接口中的方法進(jìn)行簡要說明:


LobHandler

LobHandler 接口為操作 BLOB/CLOB 提供了統(tǒng)一訪問接口,而不管底層數(shù)據(jù)庫究竟是以大對象的方式還是以一般數(shù)據(jù)類型的方式進(jìn)行操作。此外,LobHandler 還充當(dāng)了 LobCreator 的工廠類。

大部分?jǐn)?shù)據(jù)庫廠商的 JDBC 驅(qū)動程序(如 DB2)都以 JDBC 標(biāo)準(zhǔn)的 API 操作 LOB 數(shù)據(jù),但 Oracle 9i 及以前的 JDBC 驅(qū)動程序采用了自己的 API 操作 LOB 數(shù)據(jù),Oracle 9i 直接使用自己的 API 操作 LOB 數(shù)據(jù),且不允許通過 PreparedStatement 的 setAsciiStream()、setBinaryStream()、setCharacterStream() 等方法填充流數(shù)據(jù)。Spring 提供 LobHandler 接口主要是為了遷就 Oracle 特立獨(dú)行的作風(fēng)。所以 Oracle 必須使用 OracleLobHandler 實(shí)現(xiàn)類,而其它的數(shù)據(jù)庫統(tǒng)一使用 DefaultLobHandler 就可以了。

Oracle 10g 改正了 Oracle 9i 這個異化的風(fēng)格,所以 Oracle 10g 也可以使用 DefaultLobHandler。

下面,我們來看一下 LobHandler 接口的幾個重要方法:


插入LOB類型的數(shù)據(jù)

注意: 我們并不建議將二進(jìn)制文件寫入數(shù)據(jù)庫,該案例僅為演示。

假設(shè)我們artisan_lob 表,擁有兩個 LOB 字段和一個ID字段(在應(yīng)用層使用UUID生成),其中 artisan_detail是 CLOB 類型,而 artisan_attach是 BLOB 類型 。

package com.xgj.dao.lob.dao;import java.sql.PreparedStatement; import java.sql.SQLException;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback; import org.springframework.jdbc.support.lob.LobCreator; import org.springframework.jdbc.support.lob.LobHandler; import org.springframework.stereotype.Repository;import com.xgj.dao.lob.domain.Artisan;/*** * * @ClassName: ArtisanLobDaoImp* * @Description: @Repository標(biāo)注的DAO層* * @author: Mr.Yang* * @date: 2017年9月28日 下午8:15:23*/@Repository public class ArtisanLobDaoImp implements ArtisanLobDao {// 定義jdbcTemplate屬性private JdbcTemplate jdbcTemplate;// 定義LobHander屬性private LobHandler lobHandler;private static final String addArtisanLobSql = "insert into artisan_lob(artisan_id ,artisan_detail ,artisan_attach) values (?,?,?)";// 注入jdbcTemplate@Autowiredpublic void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}// 注入lobHandler@Autowiredpublic void setLobHandler(LobHandler lobHandler) {this.lobHandler = lobHandler;}@Overridepublic void addArtisanLob(final Artisan artisan) {jdbcTemplate.execute(addArtisanLobSql,new AbstractLobCreatingPreparedStatementCallback(this.lobHandler) {@Overrideprotected void setValues(PreparedStatement ps,LobCreator lobCreator) throws SQLException,DataAccessException {// 設(shè)置IDps.setString(1, artisan.getArtisanId());// 設(shè)置 CLOB 字段lobCreator.setClobAsString(ps, 2,artisan.getArtisanDetail());// 設(shè)置 BLOB 字段lobCreator.setBlobAsBytes(ps, 3,artisan.getArtisanAttach());}});}}/*** * * JdbcTemplate 中execute和update的區(qū)別:* * execute不接受參數(shù),無返回值,適用于create和drop table。* * update可以接受參數(shù),返回值為此次操作影響的記錄數(shù),適合于insert, update, 和delete等操作。* */

解讀:

首先,我們在 ArtisanLobDaoImp中引入了一個 LobHandler 屬性,并通過 JdbcTemplate#execute(String sql,AbstractLobCreatingPreparedStatementCallback lcpsc) 方法完成插入 LOB 數(shù)據(jù)的操作。

我們通過匿名內(nèi)部類的方式定義 LobCreatingPreparedStatementCallback 抽象類的子類,其構(gòu)造函數(shù)需要一個 LobHandler 入?yún)ⅰ?/font>

在匿名類中實(shí)現(xiàn)了父類的抽象方法 setValues(PreparedStatement ps,LobCreator lobCreator),在該方法中通過 lobCreator 操作 LOB 對象,我們分別通過字符串和二進(jìn)制數(shù)組填充 BLOB 和 CLOB 的數(shù)據(jù)

調(diào)整 Spring 的配置文件以配合我們剛剛定義的 ArtisanLobDaoImp。假設(shè)底層數(shù)據(jù)庫是 Oracle,可以采用以下的配置方式:

<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 掃描類包,將標(biāo)注Spring注解的類自動轉(zhuǎn)化Bean,同時完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.lob" /><!-- 使用context命名空間 配置數(shù)據(jù)庫的properties文件 --><context:property-placeholder location="classpath:spring/jdbc.properties" /><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close" p:driverClassName="${jdbc.driverClassName}"p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" /><!-- nativeJdbcExtractor 和 oracleLobHandler Bean 都設(shè)置為 lazy-init="true",這是因?yàn)?nativeJdbcExtractor 需要通過運(yùn)行期的反射機(jī)制獲取底層的 JDBC 對象,所以需要避免在 Spring 容器啟動時就實(shí)例化這兩個 Bean --><bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"lazy-init="true"/><bean id="lobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true"p:nativeJdbcExtractor-ref="nativeJdbcExtractor"/> <!-- 設(shè)置本地 Jdbc 對象抽取器 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource"/></beans>

解讀:
nativeJdbcExtractor 和 oracleLobHandler Bean 都設(shè)置為 lazy-init="true",這是因?yàn)?nativeJdbcExtractor 需要通過運(yùn)行期的反射機(jī)制獲取底層的 JDBC 對象,所以需要避免在 Spring 容器啟動時就實(shí)例化這兩個 Bean。

LobHandler 需要訪問本地 JDBC 對象,這一任務(wù)委托給 NativeJdbcExtractor Bean 來完成,因此我們在為 LobHandler 注入了一個 nativeJdbcExtractor。

最后,我們把 lobHandler Bean 通過掃描注解的方式通過方法注入的方式注入到需要進(jìn)行 LOB 數(shù)據(jù)訪問操作的 ArtisanLobDaoImp中。(同JdbcTemplate一樣)

// 注入lobHandler@Autowiredpublic void setLobHandler(LobHandler lobHandler) {this.lobHandler = lobHandler;}

如果底層數(shù)據(jù)庫是 DB2、SQL Server、MySQL 等非 Oracle 的其它數(shù)據(jù)庫,則只要簡單配置一個 DefaultLobHandler 就可以了 。 Oracle9 以后也可以采用如下配置。

<bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true"/>

經(jīng)驗(yàn)證,也可以寫入lob。

完整配置文件如下

<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 掃描類包,將標(biāo)注Spring注解的類自動轉(zhuǎn)化Bean,同時完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.lob" /><!-- 使用context命名空間 配置數(shù)據(jù)庫的properties文件 --><context:property-placeholder location="classpath:spring/jdbc.properties" /><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close" p:driverClassName="${jdbc.driverClassName}"p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" /><bean id="defaultLobHandler"class="org.springframework.jdbc.support.lob.DefaultLobHandler"lazy-init="true"/><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource"/></beans>

DefaultLobHandler 只是簡單地代理標(biāo)準(zhǔn) JDBC 的 PreparedStatement 和 ResultSet 對象,由于并不需要訪問數(shù)據(jù)庫驅(qū)動本地的 JDBC 對象,所以它不需要 NativeJdbcExtractor 的幫助


單元測試:

package com.xgj.dao.lob.dao;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.UUID;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.util.FileCopyUtils;import com.xgj.dao.lob.domain.Artisan;public class ArtisanDaoLobTest {ClassPathXmlApplicationContext ctx = null;@Beforepublic void initContext() {// 啟動Spring 容器ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/dao/lob/lobOperation.xml");System.out.println("initContext successfully");}@Testpublic void testCallProcWithCallableStatementCreator() throws IOException {ArtisanLobDaoImp artisanLobDaoImp = ctx.getBean("artisanLobDaoImp",ArtisanLobDaoImp.class);// 實(shí)例化ArtisanArtisan artisan = new Artisan();// (maven工程資源放在了 resource同名目錄下)// 設(shè)置主鍵artisan.setArtisanId(UUID.randomUUID().toString());// 讀取文本文件,設(shè)置給artisanDetailResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();Resource resource = resourcePatternResolver.getResource("classpath:com/xgj/dao/lob/dao/artisanDetail.txt");InputStream ins = resource.getInputStream();// 將 InputStream 轉(zhuǎn)成String// Scanner scanner = new Scanner(ins, "GB2312");// String artisanDetail = scanner.useDelimiter("\\A").next();String artisanDetail = inputStream2String(ins).toString();// 設(shè)置給artisanDetailartisan.setArtisanDetail(artisanDetail);// 讀取圖片信息,設(shè)置給artisanAttachClassPathResource res = new ClassPathResource("com/xgj/dao/lob/dao/281015.jpg");// 讀取圖片數(shù)據(jù)byte[] artisanAttach = FileCopyUtils.copyToByteArray(res.getInputStream());artisan.setArtisanAttach(artisanAttach);artisanLobDaoImp.addArtisanLob(artisan);System.out.println("ADD BLOB SUCCESSFULLY ");System.out.println("artisan ID:\n" + artisan.getArtisanId());System.out.println("artisan detail:\n" + artisan.getArtisanDetail());System.out.println("artisan attach length:\n"+ artisan.getArtisanAttach().length);}@Afterpublic void closeContext() {if (ctx != null) {ctx.close();}System.out.println("close context successfully");}public StringBuilder inputStream2String(InputStream ins) throws IOException {StringBuilder stringBuilder = new StringBuilder();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(ins));boolean firstLine = true;String line = null;while ((line = bufferedReader.readLine()) != null) {if (!firstLine) {stringBuilder.append(System.getProperty("line.separator"));} else {firstLine = false;}stringBuilder.append(line);}return stringBuilder;} }

輸出:

2017-09-28 21:58:48,435 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@77c55a79: startup date [Thu Sep 28 21:58:48 BOT 2017]; root of context hierarchy 2017-09-28 21:58:48,558 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/dao/lob/lobOperation.xml] initContext successfully ADD BLOB SUCCESSFULLY artisan ID: 15e396c9-9151-4689-bc4f-e1a7ba5bd55c artisan detail: JdbcTemplateexecuteupdate的區(qū)別: execute不接受參數(shù),無返回值,適用于createdrop tableupdate可以接受參數(shù),返回值為此次操作影響的記錄數(shù),適合于insert, update, 和delete等操作。 artisan attach length: 74391 2017-09-28 21:58:50,320 INFO [main] (AbstractApplicationContext.java:984) - Closing org.springframework.context.support.ClassPathXmlApplicationContext@77c55a79: startup date [Thu Sep 28 21:58:48 BOT 2017]; root of context hierarchy close context successfully

以塊數(shù)據(jù)的方式讀取LOB數(shù)據(jù)

我們可以直接用數(shù)據(jù)塊的方式讀取 LOB 數(shù)據(jù):用 String 讀取 CLOB 字段的數(shù)據(jù),用 byte[] 讀取 BLOB 字段的數(shù)據(jù)

我們新增一個接口,重寫

@Overridepublic List<Artisan> selectArtisanById(String artisanId) {List<Artisan> artisanList = jdbcTemplate.query(selectArtisanByIdSql,new Object[] { artisanId }, new RowMapper<Artisan>() {@Overridepublic Artisan mapRow(ResultSet rs, int rowNum)throws SQLException {// 以二進(jìn)制數(shù)組方式獲取 BLOB 數(shù)據(jù)。byte[] attach = lobHandler.getBlobAsBytes(rs,"artisan_attach");String artisanDetaiul = lobHandler.getClobAsString(rs,"artisan_detail");Artisan artisan = new Artisan();artisan.setArtisanAttach(attach);artisan.setArtisanDetail(artisanDetaiul);return artisan;}});return artisanList;}

通過 JdbcTemplate 的 List query(String sql, Object[] args, RowMapper rowMapper) 接口處理行數(shù)據(jù)的映射。在 RowMapper 回調(diào)的 mapRow() 接口方法中,通過 LobHandler 以 byte[] 獲取 BLOB 字段的數(shù)據(jù)。 getClobAsString獲取CLOB字段。


單元測試代碼:

@Testpublic void getArtisanListById() {List<Artisan> artisans = artisanLobDaoImp.selectArtisanById("15e396c9-9151-4689-bc4f-e1a7ba5bd55c");for (Artisan artisan : artisans) {System.out.println("artisan detail:\n" + artisan.getArtisanDetail());System.out.println("artisan attach length:\n"+ artisan.getArtisanAttach().length);}}

測試結(jié)果:

2017-09-28 22:20:41,323 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@9d1649f: startup date [Thu Sep 28 22:20:41 BOT 2017]; root of context hierarchy 2017-09-28 22:20:41,445 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/dao/lob/lobOperation.xml] initContext successfully artisan detail: One former UN official said the head of the UN in Myanmar (Burma) tried to prevent human rights advocates from visiting sensitive Rohingya areas. More than 500,000 Rohingya have fled an offensive by the military, with many now sheltering in camps in Bangladesh. The UN in Myanmar "strongly disagreed" with the BBC findings. artisan attach length: 74391 2017-09-28 22:20:43,093 INFO [main] (AbstractApplicationContext.java:984) - Closing org.springframework.context.support.ClassPathXmlApplicationContext@9d1649f: startup date [Thu Sep 28 22:20:41 BOT 2017]; root of context hierarchy close context successfully

以流數(shù)據(jù)的方式讀取LOB數(shù)據(jù)

由于 LOB 數(shù)據(jù)可能很大(如 100M),如果直接以塊的方式操作 LOB 數(shù)據(jù),需要消耗大量的內(nèi)存資源,對應(yīng)用程序整體性能產(chǎn)生巨大的沖擊。對于體積很大的 LOB 數(shù)據(jù),我們可以使用流的方式進(jìn)行訪問,減少內(nèi)存的占用。

JdbcTemplate 為此提供了一個 Object query(String sql, Object[] args, ResultSetExtractor rse) 方法.
ResultSetExtractor 接口擁有一個處理流數(shù)據(jù)的抽象類 org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor,可以通過擴(kuò)展此類用流的方式操作 LOB 字段的數(shù)據(jù)。

/*** * * @Title: getAttach* * @Description: 以流數(shù)據(jù)方式讀取 LOB 數(shù)據(jù)* * * @return: void*/public void getAttach(final String artisanId, final OutputStream os) {jdbcTemplate.query(selectAttachByIdSql, new Object[] { artisanId },new AbstractLobStreamingResultSetExtractor<Artisan>() { // 匿名內(nèi)部類// 處理未找到數(shù)據(jù)行的情況protected void handleNoRowFound()throws LobRetrievalFailureException {System.out.println("Not Found result!");}// 以流的方式處理 LOB 字段public void streamData(ResultSet rs) throws SQLException,IOException {InputStream is = lobHandler.getBlobAsBinaryStream(rs, 1);if (is != null) {FileCopyUtils.copy(is, os);}}});}

通過擴(kuò)展 AbstractLobStreamingResultSetExtractor 抽象類,在 streamData(ResultSet rs) 方法中以流的方式讀取 LOB 字段數(shù)據(jù)。這里我們又利用到了 Spring 的工具類 FileCopyUtils 將輸入流的數(shù)據(jù)拷貝到輸出流中。在 getAttach() 方法中通過入?yún)?OutputStream os 接收 LOB 的數(shù)據(jù)。

我們可以同時覆蓋抽象類中的 handleNoRowFound() 方法,定義未找到數(shù)據(jù)行時的處理邏輯。

單元測試

@Testpublic void getArtisanAttache() throws FileNotFoundException {// 定義輸出目的OutputStream os = new FileOutputStream(new File("D:/downLoad.jpg"));artisanLobDaoImp.getAttach("15e396c9-9151-4689-bc4f-e1a7ba5bd55c", os);}

運(yùn)行結(jié)果

打開如下(截圖):


示例源碼

代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster

總結(jié)

以上是生活随笔為你收集整理的Spring JDBC-使用Spring JDBC获取本地连接对象以及操作BLOB/CLOB类型数据的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。