首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring 徹底簡化 LOB 大對象操作:從 JDBC 到 ORM 的統一最佳實踐

     編輯:Java 相關知識

LOB 操作的歷史痛點

大對象 (LOB) 包含 BLOB (二進制) 與 CLOB (字符),跨數據庫類型差異大(DB2: BLOB/CLOB、MySQL: BLOB/LONGTEXT、SQL Server: IMAGE/TEXT、Oracle 9i 非標準 API)、操作方式特殊(流式讀寫、非線程安全、需獨立資源管理),傳統 JDBC 代碼繁瑣且不可移植。

Spring 核心抽象:三層協作模型

組件接口/類職責
本地 JDBC 提取器 NativeJdbcExtractor 及實現類 從連接池代理對象中解析出數據庫原生 JDBC 對象 (OracleConnection, DB2Connection 等),供特定廠商 API 調用。
LOB 創建器 LobCreator 統一 BLOB/CLOB 填充 API (setBlobAsBytes, setClobAsString, setBlobAsBinaryStream 等),屏蔽數據庫差異,非線程安全,單次操作一個 LOB。
LOB 處理器 LobHandler (工廠+訪問接口) 創建 LobCreator;提供統一讀取 API (getBlobAsBytes, getClobAsString, getBlobAsBinaryStream 等);DefaultLobHandler 適用標準 JDBC 數據庫,OracleLobHandler 專用於 Oracle 9i 非標準 API (Oracle 10g+ 可用 Default)。

Spring JDBC 模板操作 LOB 完整示例

配置數據源與 LobHandler


<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" .../>
<bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor" lazy-init="true"/>
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true"/>
<!-- Oracle 9i 需改用 OracleLobHandler 並注入 nativeJdbcExtractor -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <property name="dataSource" ref="dataSource"/>
  <property name="nativeJdbcExtractor" ref="nativeJdbcExtractor"/>
</bean>
<bean id="postDao" class="com.example.PostJdbcDao">
  <property name="jdbcTemplate" ref="jdbcTemplate"/>
  <property name="lobHandler" ref="lobHandler"/>
</bean>

插入 LOB 數據 (JdbcTemplate.execute + AbstractLobCreatingPreparedStatementCallback)


public void addPost(final Post post) {
  String sql = "INSERT INTO t_post(post_id, user_id, post_text, post_attach) VALUES(?,?,?,?)";
  jdbcTemplate.execute(sql, new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
    protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException {
      ps.setInt(1, post.getPostId());
      ps.setInt(2, post.getUserId());
      lobCreator.setClobAsString(ps, 3, post.getPostText());   // CLOB 字符串寫入
      lobCreator.setBlobAsBytes(ps, 4, post.getPostAttach());   // BLOB 字節數組寫入
      // 亦可用流式:setClobAsAsciiStream / setBlobAsBinaryStream
    }
  });
}

塊讀取 (RowMapper + getBlobAsBytes/getClobAsString)


public List getAttachs(int userId) {
  return jdbcTemplate.query("SELECT post_id, post_attach FROM t_post WHERE user_id=? AND post_attach IS NOT NULL",
    new Object[]{userId}, (rs, rowNum) -> {
      Post p = new Post();
      p.setPostId(rs.getInt(1));
      p.setPostAttach(lobHandler.getBlobAsBytes(rs, 2)); // byte[] 方式
      return p;
    });
}

流式讀取 (AbstractLobStreamingResultSetExtractor 處理大文件)


public void getAttach(int postId, OutputStream os) {
  jdbcTemplate.query("SELECT post_attach FROM t_post WHERE post_id=?", new Object[]{postId},
    new AbstractLobStreamingResultSetExtractor() {
      protected void handleNoRowFound() { throw new EmptyResultDataAccessException(1); }
      public void streamData(ResultSet rs) throws SQLException, IOException {
        InputStream is = lobHandler.getBlobAsBinaryStream(rs, 1);
        FileCopyUtils.copy(is, os); // Spring 工具類高效拷貝流
      }
    });
}

ORM 框架整合:零侵入式 LOB 映射

JPA / Hibernate:注解 + Spring UserType


// JPA 實體注解
@Entity @Table(name="T_POST")
public class Post {
  @Lob @Basic(fetch=FetchType.EAGER) @Column(name="POST_TEXT", columnDefinition="LONGTEXT")
  private String postText;
  
  @Lob @Basic(fetch=FetchType.LAZY) @Column(name="POST_ATTACH", columnDefinition="BLOB")
  private byte[] postAttach;
}

// Hibernate 映射文件使用 Spring 提供的 UserType 實現
<property name="postText" column="post_text" type="org.springframework.orm.hibernate3.support.ClobStringType"/>
<property name="postAttach" column="post_attach" type="org.springframework.orm.hibernate3.support.BlobByteArrayType"/>

關鍵配置:在 LocalSessionFactoryBean 注入 lobHandler,並在事務環境下操作(否則拋異常)。Oracle 9i 同樣需配置 OracleLobHandler + NativeJdbcExtractor。

iBatis / MyBatis:TypeHandler 統一映射


<resultMap id="result" class="Post">
  <result property="postText" column="post_text" typeHandler="org.springframework.orm.ibatis.support.ClobStringTypeHandler"/>
  <result property="postAttach" column="post_attach" typeHandler="org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler"/>
</resultMap>
<insert id="addPost">
  INSERT INTO t_post(user_id, post_text, post_attach) 
  VALUES(#userId#, #postText,handler=ClobStringTypeHandler#, #postAttach,handler=BlobByteArrayTypeHandler#)
</insert>

或在 sql-map-config.xml 全局註冊:<typeHandler jdbcType="CLOB" javaType="java.lang.String" callback="ClobStringTypeHandler"/>。SqlMapClientFactoryBean 需注入 lobHandler。

進階提示與避坑指南

  • 流式讀寫大 LOB:超過 100MB 必須用流,否則 OOM。Spring FileCopyUtils、IOUtils 輔助拷貝。
  • 事務邊界:LOB 操作需在同一事務中完成(連接不可關閉),配置 DataSourceTransactionManager 或 JTA。
  • Oracle 10g+ 遷移:移除 OracleLobHandler,改用 DefaultLobHandler,簡化配置。
  • Hibernate 流式 LOB:Spring 官方未提供流式 UserType,可參考 Atlassian 開源的 BlobInputStream 類型實現。
  • 連接池配置:DBCP、C3P0、HikariCP 需配對對應的 NativeJdbcExtractor 實現類。

總結

Spring 通過 NativeJdbcExtractor 解決原生連接獲取、LobHandler/LobCreator 雙重抽象屏蔽數據庫差異、JdbcTemplate 回調模板簡化流程,並為主流 ORM 提供開箱即用的類型映射實現。開發者僅需根據底層數據庫選擇正確的 LobHandler 實現並注入事務管理器,即可像操作普通字段一樣處理 LOB,徹底消除跨數據庫移植痛點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved