大對象 (LOB) 包含 BLOB (二進制) 與 CLOB (字符),跨數據庫類型差異大(DB2: BLOB/CLOB、MySQL: BLOB/LONGTEXT、SQL Server: IMAGE/TEXT、Oracle 9i 非標準 API)、操作方式特殊(流式讀寫、非線程安全、需獨立資源管理),傳統 JDBC 代碼繁瑣且不可移植。
| 組件 | 接口/類 | 職責 |
|---|---|---|
| 本地 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)。 |
<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>
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 工具類高效拷貝流
}
});
}
// 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。
<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。
FileCopyUtils、IOUtils 輔助拷貝。DataSourceTransactionManager 或 JTA。OracleLobHandler,改用 DefaultLobHandler,簡化配置。BlobInputStream 類型實現。NativeJdbcExtractor 實現類。Spring 通過 NativeJdbcExtractor 解決原生連接獲取、LobHandler/LobCreator 雙重抽象屏蔽數據庫差異、JdbcTemplate 回調模板簡化流程,並為主流 ORM 提供開箱即用的類型映射實現。開發者僅需根據底層數據庫選擇正確的 LobHandler 實現並注入事務管理器,即可像操作普通字段一樣處理 LOB,徹底消除跨數據庫移植痛點。