javascript
Spring JDBC-使用Spring JDBC获取本地连接对象以及操作BLOB/CLOB类型数据
- 概述
- 如何獲取本地數據連接
- 示例從DBCP數據源中獲取Oracle的本地連接對象
- 相關接口操作
- LobCreator
- LobHandler
- 插入LOB類型的數據
- 以塊數據的方式讀取LOB數據
- 以流數據的方式讀取LOB數據
- 示例源碼
概述
我們在Spring-使用Spring JDBC訪問數據庫使用JDBC進行了CRUD(Create Retrieve Update Delete增刪改查)以及調用存過的操作,這里我們將進一步了解一些高級的數據庫操作知識,包括獲取本地數據連接進行數據庫相關的操作和如何操作BLOB、CLBO這些LOB數據。
LOB 代表大對象數據,包括 BLOB 和 CLOB 兩種類型。
- BLOB 用于存儲大塊的二進制數據,如圖片數據,視頻數據等(議案不宜將文件存儲到數據中,而應該存儲到專門的文件服務器中)
- CLOB 用于存儲長文本數據,如產品的詳細描述等。
值得注意的是:在不同的數據庫中,大對象對應的字段類型是不盡相同的,如 DB2 對應 BLOB/CLOB,MySql 對應 BLOB/LONGTEXT,SqlServer 對應 IMAGE/TEXT。
需要指出的是,有些數據庫的大對象類型可以象簡單類型一樣訪問,如 MySql 的 LONGTEXT 的操作方式和 VARCHAR 類型一樣。在一般情況下, LOB 類型數據的訪問方式不同于其它簡單類型的數據,我們經常會以流的方式操作 LOB 類型的數據。
此外,LOB 類型數據的訪問不是線程安全的,需要為其單獨分配相應的數據庫資源,并在操作完成后釋放資源。
Spring 在 org.springframework.jdbc.support.lob 包中為我們提供了相應的幫助類,以便解決上述疑難雜癥。
Spring 大大降低了我們處理 LOB 數據的難度。
首先,Spring 提供了 NativeJdbcExtractor 接口,我們可以在不同環境里選擇相應的實現類從數據源中獲取本地 JDBC對象;
其次,Spring 通過 LobCreator 接口取消了不同數據廠商操作 LOB 數據的差別,并提供了創建 LobCreator 和LobHandler 接口,我們只要根據底層數據庫類型選擇合適的 LobHandler 進行配置即可。
如何獲取本地數據連接
我們知道,在 Web 應用服務器或 Spring 中配置數據源時,從數據源中返回的數據連接對象是本地 JDBC 對象(如 DB2Connection、OracleConnection)的代理類,這是因為數據源需要改變數據連接原有的行為以便施加額外的控制,比如在調用Connection#close()方法時,將數據連接返還到連接池中而非將其關閉。
但是我們在某些情況下,希望得到被代理前的本地JDBC對象,比如OracleConnection或者OracleResultSet,以便調用這些驅動程序廠商相關的API完成一些特殊的操作。
為了獲取本地JDBC對象,Spring在org.framework.jdbc.support.nativejdbc包下定義了NativeJdbcExtractor接口并提供了實現類。 NativeJdbcExtractor接口定義了從數據源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 對象
有些簡單的數據源僅對 Connection 對象進行代理,這時可以直接使用 SimpleNativeJdbcExtractor 實現類。但有些數據源(如 Jakarta Commons DBCP)會對所有的 JDBC 對象進行代理,這時,就需要根據具體的情況選擇適合的抽取器實現類了。下表列出了不同數據源本地 JDBC 對象抽取器的實現類:
| WebSphere 4 及以上版本的數據源 | org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor |
| WebLogic 6.1+ 及以上版本的數據源 | org.springframework.jdbc.support.nativejdbc.WebLogicNativeJdbcExtractor |
| JBoss 3.2.4 及以上版本的數據源 | org.springframework.jdbc.support.nativejdbc.JBossNativeJdbcExtractor |
| C3P0 數據源 | org.springframework.jdbc.support.nativejdbc.C3P0NativeJdbcExtractor |
| DBCP 數據源 | org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor |
| ObjectWeb 的 XAPool 數據源 | org.springframework.jdbc.support.nativejdbc.XAPoolNativeJdbcExtractor |
示例:從DBCP數據源中獲取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標注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: 要想使這個類正確運行,JdbcTemplate模板配置必須調整,具體見conf-getLocalConnObj.xml* * * @return: void*/public OracleConnection getOracleNativeConn() {OracleConnection oracleConnection = null;try {// 使用DataSourceUtils 從JdbcTemplate中獲取數據連接Connection connection = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());// 使用模板類的本地JDBC抽取器獲取本地連接connection = jdbcTemplate.getNativeJdbcExtractor().getNativeConnection(connection);// 強制類型轉換oracleConnection = (OracleConnection) connection;// 使用本地對象,調用API完成業務操作(此處省略) 比如使用OracleConnection特殊 API操作lob} catch (CannotGetJdbcConnectionException | SQLException e) {e.printStackTrace();}return oracleConnection;} }我們通過 DataSourceUtils 獲取當前線程綁定的數據連接,為了使用線程上下文相關的事務,通過 DataSourceUtils 從數據源中獲取連接是正確的做法,如果直接通過 dateSource 獲取連接,則將得到一個和當前線程上下文無關的數據連接實例。
JdbcTemplate 可以在配置時注入一個本地 JDBC 對象抽取器,要使上述代碼 正確運行,我們必須進行如下配置:
<?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"><!-- 掃描類包,將標注Spring注解的類自動轉化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文件中配置數據庫的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數據源的JDBC本地對象抽取器 --><bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"></bean><!-- 配置Jdbc模板 設置抽取器 --><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");} }運行結果
單元測試通過,說明oracleConnection不為空,我們獲取到了oracleConnection對象。
相關接口操作
LobCreator
雖然 JDBC 定義了兩個操作 LOB 類型的接口:java.sql.Blob 和 java.sql.Clob,但有些廠商的 JDBC 驅動程序并不支持這兩個接口。為此,Spring 定義了一個獨立于 java.sql.Blob/Clob 的 LobCreator 接口,以統一的方式操作各種數據庫的 LOB 類型數據。
因為 LobCreator 本身持有 LOB 所對應的數據庫資源,所以它不是線程安全的,一個 LobCreator 只能操作一個 LOB 數據。
為了方便在 PreparedStatement 中使用 LobCreator,我們可以直接使用如下方法。
JdbcTemplate#execute(String sql,AbstractLobCreatingPreparedStatementCallback lcpsc)下面對 LobCreator 接口中的方法進行簡要說明:
LobHandler
LobHandler 接口為操作 BLOB/CLOB 提供了統一訪問接口,而不管底層數據庫究竟是以大對象的方式還是以一般數據類型的方式進行操作。此外,LobHandler 還充當了 LobCreator 的工廠類。
大部分數據庫廠商的 JDBC 驅動程序(如 DB2)都以 JDBC 標準的 API 操作 LOB 數據,但 Oracle 9i 及以前的 JDBC 驅動程序采用了自己的 API 操作 LOB 數據,Oracle 9i 直接使用自己的 API 操作 LOB 數據,且不允許通過 PreparedStatement 的 setAsciiStream()、setBinaryStream()、setCharacterStream() 等方法填充流數據。Spring 提供 LobHandler 接口主要是為了遷就 Oracle 特立獨行的作風。所以 Oracle 必須使用 OracleLobHandler 實現類,而其它的數據庫統一使用 DefaultLobHandler 就可以了。
Oracle 10g 改正了 Oracle 9i 這個異化的風格,所以 Oracle 10g 也可以使用 DefaultLobHandler。
下面,我們來看一下 LobHandler 接口的幾個重要方法:
插入LOB類型的數據
注意: 我們并不建議將二進制文件寫入數據庫,該案例僅為演示。
假設我們artisan_lob 表,擁有兩個 LOB 字段和一個ID字段(在應用層使用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標注的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 {// 設置IDps.setString(1, artisan.getArtisanId());// 設置 CLOB 字段lobCreator.setClobAsString(ps, 2,artisan.getArtisanDetail());// 設置 BLOB 字段lobCreator.setBlobAsBytes(ps, 3,artisan.getArtisanAttach());}});}}/*** * * JdbcTemplate 中execute和update的區別:* * execute不接受參數,無返回值,適用于create和drop table。* * update可以接受參數,返回值為此次操作影響的記錄數,適合于insert, update, 和delete等操作。* */解讀:
首先,我們在 ArtisanLobDaoImp中引入了一個 LobHandler 屬性,并通過 JdbcTemplate#execute(String sql,AbstractLobCreatingPreparedStatementCallback lcpsc) 方法完成插入 LOB 數據的操作。
我們通過匿名內部類的方式定義 LobCreatingPreparedStatementCallback 抽象類的子類,其構造函數需要一個 LobHandler 入參。
在匿名類中實現了父類的抽象方法 setValues(PreparedStatement ps,LobCreator lobCreator),在該方法中通過 lobCreator 操作 LOB 對象,我們分別通過字符串和二進制數組填充 BLOB 和 CLOB 的數據
調整 Spring 的配置文件以配合我們剛剛定義的 ArtisanLobDaoImp。假設底層數據庫是 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"><!-- 掃描類包,將標注Spring注解的類自動轉化Bean,同時完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.lob" /><!-- 使用context命名空間 配置數據庫的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 都設置為 lazy-init="true",這是因為 nativeJdbcExtractor 需要通過運行期的反射機制獲取底層的 JDBC 對象,所以需要避免在 Spring 容器啟動時就實例化這兩個 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"/> <!-- 設置本地 Jdbc 對象抽取器 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource"/></beans>解讀:
nativeJdbcExtractor 和 oracleLobHandler Bean 都設置為 lazy-init="true",這是因為 nativeJdbcExtractor 需要通過運行期的反射機制獲取底層的 JDBC 對象,所以需要避免在 Spring 容器啟動時就實例化這兩個 Bean。
LobHandler 需要訪問本地 JDBC 對象,這一任務委托給 NativeJdbcExtractor Bean 來完成,因此我們在為 LobHandler 注入了一個 nativeJdbcExtractor。
最后,我們把 lobHandler Bean 通過掃描注解的方式通過方法注入的方式注入到需要進行 LOB 數據訪問操作的 ArtisanLobDaoImp中。(同JdbcTemplate一樣)
// 注入lobHandler@Autowiredpublic void setLobHandler(LobHandler lobHandler) {this.lobHandler = lobHandler;}如果底層數據庫是 DB2、SQL Server、MySQL 等非 Oracle 的其它數據庫,則只要簡單配置一個 DefaultLobHandler 就可以了 。 Oracle9 以后也可以采用如下配置。
<bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true"/>經驗證,也可以寫入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"><!-- 掃描類包,將標注Spring注解的類自動轉化Bean,同時完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.lob" /><!-- 使用context命名空間 配置數據庫的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 只是簡單地代理標準 JDBC 的 PreparedStatement 和 ResultSet 對象,由于并不需要訪問數據庫驅動本地的 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);// 實例化ArtisanArtisan artisan = new Artisan();// (maven工程資源放在了 resource同名目錄下)// 設置主鍵artisan.setArtisanId(UUID.randomUUID().toString());// 讀取文本文件,設置給artisanDetailResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();Resource resource = resourcePatternResolver.getResource("classpath:com/xgj/dao/lob/dao/artisanDetail.txt");InputStream ins = resource.getInputStream();// 將 InputStream 轉成String// Scanner scanner = new Scanner(ins, "GB2312");// String artisanDetail = scanner.useDelimiter("\\A").next();String artisanDetail = inputStream2String(ins).toString();// 設置給artisanDetailartisan.setArtisanDetail(artisanDetail);// 讀取圖片信息,設置給artisanAttachClassPathResource res = new ClassPathResource("com/xgj/dao/lob/dao/281015.jpg");// 讀取圖片數據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: JdbcTemplate 中execute和update的區別: execute不接受參數,無返回值,適用于create和drop table。 update可以接受參數,返回值為此次操作影響的記錄數,適合于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以塊數據的方式讀取LOB數據
我們可以直接用數據塊的方式讀取 LOB 數據:用 String 讀取 CLOB 字段的數據,用 byte[] 讀取 BLOB 字段的數據
我們新增一個接口,重寫
@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 {// 以二進制數組方式獲取 BLOB 數據。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) 接口處理行數據的映射。在 RowMapper 回調的 mapRow() 接口方法中,通過 LobHandler 以 byte[] 獲取 BLOB 字段的數據。 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);}}測試結果:
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以流數據的方式讀取LOB數據
由于 LOB 數據可能很大(如 100M),如果直接以塊的方式操作 LOB 數據,需要消耗大量的內存資源,對應用程序整體性能產生巨大的沖擊。對于體積很大的 LOB 數據,我們可以使用流的方式進行訪問,減少內存的占用。
JdbcTemplate 為此提供了一個 Object query(String sql, Object[] args, ResultSetExtractor rse) 方法.
ResultSetExtractor 接口擁有一個處理流數據的抽象類 org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor,可以通過擴展此類用流的方式操作 LOB 字段的數據。
通過擴展 AbstractLobStreamingResultSetExtractor 抽象類,在 streamData(ResultSet rs) 方法中以流的方式讀取 LOB 字段數據。這里我們又利用到了 Spring 的工具類 FileCopyUtils 將輸入流的數據拷貝到輸出流中。在 getAttach() 方法中通過入參 OutputStream os 接收 LOB 的數據。
我們可以同時覆蓋抽象類中的 handleNoRowFound() 方法,定義未找到數據行時的處理邏輯。
單元測試
@Testpublic void getArtisanAttache() throws FileNotFoundException {// 定義輸出目的OutputStream os = new FileOutputStream(new File("D:/downLoad.jpg"));artisanLobDaoImp.getAttach("15e396c9-9151-4689-bc4f-e1a7ba5bd55c", os);}運行結果
打開如下(截圖):
示例源碼
代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster
總結
以上是生活随笔為你收集整理的Spring JDBC-使用Spring JDBC获取本地连接对象以及操作BLOB/CLOB类型数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring JDBC-使用Spring
- 下一篇: Spring JDBC-自增键和行集Ro