Spring JDBC參數(shù)處理與嵌入式數(shù)據庫的實戰(zhàn)指南
以下內容是 Spring Framework 官方文檔中關于 JDBC 支持的兩個重要章節(jié)(3.8 和 3.9),主要講解了在使用 Spring 的 JdbcTemplate 和相關工具時,如何處理一些常見的、復雜的數(shù)據庫操作問題,以及如何使用嵌入式數(shù)據庫(Embedded Database)進行開發(fā)和測試。
下面我將用通俗易懂的方式,幫你系統(tǒng)地理解這段內容的核心思想和關鍵知識點,并說明它們在實際開發(fā)中的意義。
?? 一、整體結構概覽
| 章節(jié) | 主題 | 用途 |
|---|---|---|
| 3.8 | 參數(shù)與數(shù)據值處理常見問題 | 解決 JDBC 操作中的特殊場景 |
| 3.9 | 嵌入式數(shù)據庫支持 | 快速搭建輕量級數(shù)據庫用于開發(fā)/測試 |
?? 二、深入理解 3.8 節(jié):參數(shù)與數(shù)據值處理的常見問題
? 3.8.1 提供 SQL 類型信息(SQL Type for Parameters)
? 問題背景:
Java 中的 null 值傳給數(shù)據庫時,JDBC 不知道它對應的是 VARCHAR 還是 INTEGER,所以無法正確設置類型。這會導致插入 NULL 失敗。
? 解決方案:
Spring 允許你在設置參數(shù)時顯式指定 SQL 類型(來自 java.sql.Types 的常量)。
三種方式:
int 數(shù)組方式(適用于位置參數(shù))
jdbcTemplate.update(
"INSERT INTO users(name, age) VALUES(?, ?)",
"Tom", 25,
new int[]{Types.VARCHAR, Types.INTEGER} // 顯式指定類型
);使用 SqlParameterValue 包裝參數(shù)
new SqlParameterValue(Types.VARCHAR, "Tom")
可以更精細控制,比如設置 scale(小數(shù)位數(shù))。
命名參數(shù) + SqlParameterSource
使用 MapSqlParameterSource 或 BeanPropertySqlParameterSource 并注冊類型:
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("name", "Tom", Types.VARCHAR);
params.addValue("age", 25, Types.INTEGER);
?? 關鍵點:主要用于處理 NULL 插入或類型模糊的情況。
? 3.8.2 處理 BLOB 和 CLOB(大對象)
? 什么是 BLOB / CLOB?
- BLOB:Binary Large Object → 圖片、音頻、PDF 等二進制文件。
- CLOB:Character Large Object → 大段文本(如文章、日志)。
? 為什么需要特殊處理?
普通 String 或 byte[] 在讀寫大文件時會占用大量內存。理想做法是流式處理。
? Spring 的解決方案:LobHandler+LobCreator
| 功能 | 接口 | 方法 |
|---|---|---|
| 寫入 LOB | LobCreator | setBlobAsBinaryStream, setClobAsCharacterStream |
| 讀取 LOB | LobHandler | getBlobAsBytes, getClobAsString |
示例:插入圖片和文本文件
jdbcTemplate.execute(
"INSERT INTO docs(id, content, image) VALUES (?, ?, ?)",
new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lc) throws SQLException {
ps.setLong(1, 1L);
lc.setClobAsCharacterStream(ps, 2, reader, (int)file.length()); // CLOB
lc.setBlobAsBinaryStream(ps, 3, inputStream, (int)image.length()); // BLOB
}
}
);?? 注意:
lobHandler通常是DefaultLobHandler,但注意它不支持流式讀取超過Integer.MAX_VALUE的數(shù)據。
? 優(yōu)勢:避免一次性加載整個大文件到內存。
? 3.8.3 為IN子句傳遞列表(List in IN Clause)
? 問題:
SQL 不允許預編譯語句動態(tài)占位符數(shù)量,比如:
SELECT * FROM users WHERE id IN (?)
但如果要傳 (1,2,3),就需要三個 ?。
? Spring 的解決方案:
自動拼接 SQL,根據 List 長度生成對應數(shù)量的 ?。
List<Long> ids = Arrays.asList(1L, 2L, 3L);
List<User> users = jdbcTemplate.query(
"SELECT * FROM users WHERE id IN (:ids)",
new MapSqlParameterSource("ids", ids),
userRowMapper
);
? 使用 NamedParameterJdbcTemplate 自動處理。
?? 注意限制:
- 大多數(shù)數(shù)據庫對
IN列表有上限(如 Oracle 是 1000)。 - 如果超過,應分批查詢。
? 3.8.4 存儲過程調用中的復雜類型(Complex Types)
? 問題:
某些數(shù)據庫(如 Oracle)支持自定義對象類型(如 STRUCT, ARRAY),Java 如何傳遞和接收?
? 解決方案:
輸出參數(shù)(返回復雜類型)→ SqlReturnType
declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE",
(cs, idx, sqlType, typeName) -> {
STRUCT struct = (STRUCT) cs.getObject(idx);
// 轉換為 Java 對象
return new TestItem(...);
}));
輸入參數(shù)(傳入復雜類型)→ SqlTypeValue
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, ...) {
StructDescriptor desc = new StructDescriptor("ITEM_TYPE", conn);
return new STRUCT(desc, conn, new Object[]{id, name, date});
}
};
然后作為參數(shù)傳入:
Map<String, Object> in = new HashMap<>();
in.put("item", value);
storedProc.execute(in);
? 適用于 Oracle、PostgreSQL 等支持復雜類型的數(shù)據庫。
??? 三、深入理解 3.9 節(jié):嵌入式數(shù)據庫(Embedded Database)
? 3.9.1 為什么要用嵌入式數(shù)據庫?
- 輕量快速:無需安裝 MySQL/PostgreSQL。
- 啟動快:內存中運行,適合單元測試。
- 隔離性好:每個測試獨立數(shù)據庫,不污染真實數(shù)據。
- 便于自動化測試:CI/CD 中無需外部依賴。
常見用途:單元測試、集成測試、原型開發(fā)
? 3.9.2 通過 XML 創(chuàng)建嵌入式數(shù)據庫
<jdbc:embedded-database id="dataSource" generate-name="true">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>- 自動創(chuàng)建 HSQL 內存數(shù)據庫。
- 執(zhí)行建表腳本和測試數(shù)據腳本。
- 生成一個
DataSourceBean,可注入 DAO。
? 3.9.3 編程方式創(chuàng)建(推薦用于測試)
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.addScript("schema.sql")
.addScripts("data.sql")
.build();? EmbeddedDatabase 實現(xiàn)了 DataSource,可以直接傳給 JdbcTemplate。
? 測試結束后記得 db.shutdown()。
? 3.9.4 支持的嵌入式數(shù)據庫類型
| 類型 | 說明 |
|---|---|
| HSQL | 默認,老牌嵌入式數(shù)據庫 |
| H2 | 功能強大,支持 MySQL 模式、Web 控制臺 |
| Derby | Apache 開源,Java 編寫 |
推薦使用 H2,功能最全,調試方便。
? 3.9.5 測試數(shù)據訪問邏輯(最佳實踐模板)
class DataAccessTest {
private EmbeddedDatabase db;
@BeforeEach
void setUp() {
db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.addDefaultScripts() // 自動加載 schema.sql + data.sql
.build();
}
@Test
void shouldFindUserById() {
JdbcTemplate template = new JdbcTemplate(db);
List<User> users = template.query("SELECT * FROM users WHERE id = ?",
new Object[]{1}, userRowMapper);
assertThat(users).hasSize(1);
}
@AfterEach
void tearDown() {
db.shutdown();
}
}? 這是典型的 集成測試模板。
? 3.9.6 為什么需要“唯一數(shù)據庫名”?
? 問題:
多個測試類都用相同的配置創(chuàng)建嵌入式數(shù)據庫,Spring 會復用同一個實例(因為默認名字是 testdb),導致數(shù)據污染。
? 解決方案:
啟用唯一名稱生成:
.generateUniqueName(true)
或 XML 中:
<jdbc:embedded-database generate-name="true">
? 每次創(chuàng)建都是獨立數(shù)據庫,避免沖突。
? 3.9.7 擴展支持(高級)
你可以自定義:
- 新的嵌入式數(shù)據庫類型(實現(xiàn)
EmbeddedDatabaseConfigurer) - 新的
DataSource工廠(如連接池)
一般開發(fā)者不需要做,但框架開發(fā)者可以貢獻擴展。
?? 四、總結:這些知識能用來做什么?
| 技術點 | 實際應用場景 |
|---|---|
SqlParameterValue | 插入 NULL 字段,確保類型正確 |
LobHandler | 存儲圖片、文檔、大文本(如博客內容) |
IN 子句傳 List | 批量查詢用戶、訂單等 |
SqlTypeValue | 調用 Oracle 存儲過程傳對象 |
| 嵌入式數(shù)據庫 | 單元測試、快速原型、CI/CD 自動化 |
?? 五、學習建議
- 先掌握
JdbcTemplate基礎用法(查詢、更新) - 再學命名參數(shù)和
IN子句(日常開發(fā)高頻) - 測試時使用嵌入式數(shù)據庫(提升測試效率)
- 只有對接 Oracle 存儲過程時才深入
SqlTypeValue
?? 補充:現(xiàn)代 Spring 推薦替代方案
雖然這些技術仍然有效,但在現(xiàn)代 Spring Boot 項目中:
| 傳統(tǒng)技術 | 現(xiàn)代替代 |
|---|---|
JdbcTemplate + LobHandler | 使用文件存儲服務(如 MinIO、S3)+ 數(shù)據庫存路徑 |
| 手動創(chuàng)建嵌入式數(shù)據庫 | 使用 @DataJpaTest + H2(Spring Boot 自動配置) |
SqlTypeValue | 使用 JPA + Hibernate 映射復雜類型,或 MyBatis |
如果你告訴我你目前在做什么項目(比如:Web 后端?單元測試?還是對接 Oracle?),我可以給出更具體的建議 ??
到此這篇關于Spring JDBC實戰(zhàn):參數(shù)處理與嵌入式數(shù)據庫的文章就介紹到這了,更多相關Spring JDBC參數(shù)處理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Spring學習JdbcTemplate數(shù)據庫事務參數(shù)
- Java使用Spring JdbcTemplate向in語句中傳遞參數(shù)的教程詳解
- Spring jdbc具名參數(shù)使用方法詳解
- SpringBoot中使用JdbcTemplate訪問Oracle數(shù)據庫的案例詳解
- SpringBoot使用JdbcTemplate操作數(shù)據庫
- Spring boot 使用JdbcTemplate訪問數(shù)據庫
- Spring Boot 與 Kotlin 使用JdbcTemplate連接MySQL數(shù)據庫的方法
- Spring的連接數(shù)據庫以及JDBC模板(實例講解)
相關文章
Spring Boot 3 中 WebFilter 的執(zhí)行順序流程分析
Spring Boot中管理WebFilter順序,默認為Integer.MAX_VALUE,需顯式使用@Order或Ordered接口定義優(yōu)先級,請求階段按順序值從小到大執(zhí)行,響應階段按從大到小,相同順序值依賴Bean注冊順序,本文詳細說明在Spring Boot 3中如何管理這些過濾器的執(zhí)行流程,需要的朋友一起看看吧2025-08-08

