目录
- 一、整体结构概览
- 二、深入理解 3.8 节:参数与数据值处理的常见问题
- ✅ 3.8.1 提供 SQL 类型信息(SQL Type for Parameters)
- ❓ 问题背景:
- ✅ 解决方案:
- ✅ 3.8.2 处理 BLOB 和 CLOB(大对象)
- ❓ 什么是 BLOB / CLOB?
- ❓ 为什么需要特殊处理?
- ✅ Spring 的解决方案:LobHandler+LobCreator
- ✅ 3.8.3 为IN子句传递列表(List in IN Clause)
- ❓ 问题:
- ✅ Spring 的解决方案:
- ✅ 3.8.4 存储过程调用中的复杂类型(Complex Types)
- ❓ 问题:
- ✅ 解决方案:
- ️ 三、深入理解 3.9 节:嵌入式数据库(Embedded Database)
- ✅ 3.9.1 为什么要用嵌入式数据库?
- ✅ 3.9.2 通过 XML 创建嵌入式数据库
- ✅ 3.9.3 编程方式创http://www.devze.com建(推荐用于测试)
- ✅ 3.9.4 支持的嵌入式数据库类型
- ✅ 3.9.5 测试数据访问逻辑(最佳实践模板)
- ✅ 3.9.6 为什么需要“唯一数据库名”?
- ❓ 问题:
- ✅ 解决方案:
- ✅ 3.9.7 扩展支持(高级)
- 四、总结:这些知识能用来做什么?
- 五、学习建议
- 补充:现代 Spring 推荐替代方案
以下内容是 Spring Framework 官方文档中关于 JDBC 支持的两个重要章节(3.8 和 3.9),主要讲解了在使用 Spring 的 JdbcTemplate 和相关工具时,如何处理一些常见的、复杂的数据库操作问题,以及如何使用嵌入式数据库(Embedded Database)进行开发和测试。
下面我将用通俗易懂的方式,帮你系统地理解这段内容的核心思想和关键知识点,并说明它们在实际开发中的意义。
一、整体结构概览
| 章节 | 主题 | 用途 |
|---|---|---|
| 3.8 | 参数与数据值处理常见问题 | 解决 JDBC 操作中的特殊场景 |
| 3.9 | 嵌入式数据库支持 | 快速搭建轻量级数据库用于开发/测试 |
二、深入理解 3.8 节:参数与数据值处理的常见问题
✅ 3.8.1 提供 SQL 类型信息(SQL Type for Parameters)
❓ 问题背景:
Java 中的 null 值传给数据库时,JDBC 不知道它对应的是 VARCHAR 还是 INTEGER,所以无法正确设置类型。这会导致插入 NULL 失败。
✅ 解决方案:
Spring 允许你在设置参数时显式指定 SQL 类型(来自 java.sql.Types 的常量)。
三种方式:
int 数组方式(适用于位置参数)
jdbcTemplate.update(
"INSERT INTO users(name, age) VALUES(?, ?)",
"Tom", 25,
new int[]{Types.VARCHAR, Types.INTEGER} // 显式指定类型
);
使用 SqlParameterValue 包装参数
new SqlParameterValue(Types.VARCHAR, "Tom")
可以更精细控制,比如设置 scale(小数位数)。
命名参数 + 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 Obje编程ct → 图片、音频、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 setV编程alues(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的数据。
✅ 优势:避免一次性加载整个大文件到内存。
✅ 3.8.3 为IN子句传递列表(List in IN Clause)
❓ 问题:
SQL 不允许预编译语句动态占位符数量,比如:
SELECT * FROM users WHERE id IN (?)
但如果要传 (1,2,3),就需要三个 ?。
✅ Spring 的解决方案:
自动拼接 SQL,根据 List 长度生成对应数量的 ?。
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 自动处理。
⚠️ 注意限制:
- 大多数数据库对
IN列表有上限(如 oracle 是 1000)。 - 如果超过,应分批查询。
✅ 3.8.4 存储过程调用中的复杂类型(Complex Types)
❓ 问题:
某些数据库(如 Oracle)支持自定义对象类型(如 STRUCT, ARRAY),Java 如何传递和接收?
✅ 解决方案:
输出参数(返回复杂类型)→ SqlReturnType
declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE",
(cs, idx, sqlType, typeName) -> {
STRUCT struct = (STRUCT) cs.getObject(idx);
// 转换为 Java 对象
return new TestItem(...);
}));
输入参数(传入复杂类型)→ 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});
}
};
然后作为参数传入:
Map<String, Object> in = new HashMap<>();
in.put("item", value);
storedProc.execute(in);
✅ 适用于 Oracle、PostgreSQL 等支持复杂类型的数据库。
️ 三、深入理解 3.9 节:嵌入式数据库(Embedded Database)
✅ 3.9.1 为什么要用嵌入式数据库?
- 轻量快速:无需安装 mysql/PostgreSQL。
- 启动快:内存中运行,适合单元测试。
- 隔离性好:每个测试独立数据库,不污染真实数据。
- 便于自动化测试:CI/CD 中无需外部依赖。
常见用途:单元测试、集成测试、原型开发
✅ 3.9.2 通过 XML 创建嵌入式数据库
<jdbc:embedded-database id="dataSource" generate-name="true">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:te编程st-data.sql"/>
</jdbc:embedded-database>
- 自动创建 HSQL 内存数据库。
- 执行建表脚本和测试数据脚本。
- 生成一个
DataSourceBean,可注入 DAO。
✅ 3.9.3 编程方式创建(推荐用于测试)
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.addScript("schema.sql")
.addScripts("data.sql")
.build();
✅ EmbeddedDatabase 实现了 DataSource,可以直接传给 JdbcTemplate。
✅ 测试结束后记得 db.shutdown()。
✅ 3.9.4 支持的嵌入式数据库类型
| 类型 | 说明 |
|---|---|
| HSQL | 默认,老牌嵌入式数据库 |
| H2 | 功能强大,支持 MySQL 模式、Web 控制台 |
| Derby | Apache 开源,Java 编写 |
推荐使用 H2,功能最全,调试方便。
✅ 3.9.5 测试数据访问逻辑(最佳实践模板)
class DataAccessT编程est {
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 为什么需要“唯一数据库名”?
❓ 问题:
多个测试类都用相同的配置创建嵌入式数据库,Spring 会复用同一个实例(因为默认名字是 testdb),导致数据污染。
✅ 解决方案:
启用唯一名称生成:
.generateUniqueName(true)
或 XML 中:
<jdbc:embedded-database generate-name="true">
✅ 每次创建都是独立数据库,避免冲突。
✅ 3.9.7 扩展支持(高级)
你可以自定义:
- 新的嵌入式数据库类型(实现
EmbeddedDatabaseConfigurer) - 新的
DataSource工厂(如连接池)
一般开发者不需要做,但框架开发者可以贡献扩展。
四、总结:这些知识能用来做什么?
| 技术点 | 实际应用场景 |
|---|---|
SqlParameterValue | 插入 NULL 字段,确保类型正确 |
LobHandler | 存储图片、文档、大文本(如博客内容) |
IN 子句传 List | 批量查询用户、订单等 |
SqlTypeValue | 调用 Oracle 存储过程传对象 |
| 嵌入式数据库 | 单元测试、快速原型、CI/CD 自动化 |
五、学习建议
- 先掌握
JdbcTemplate基础用法(查询、更新) - 再学命名参数和
IN子句(日常开发高频) - 测试时使用嵌入式数据库(提升测试效率)
- 只有对接 Oracle 存储过程时才深入
SqlTypeValue
补充:现代 Spring 推荐替代方案
虽然这些技术仍然有效,但在现代 Spring Boot 项目中:
| 传统技术 | 现代替代 |
|---|---|
JdbcTemplate + LobHandler | 使用文件存储服务(如 MinIO、S3)+ 数据库存路径 |
| 手动创建嵌入式数据库 | 使用 @DataJpaTest + H2(Spring Boot 自动配置) |
SqlTypeValue | 使用 JPA + Hibernate 映射复杂类型,或 MyBATis |
如果你告诉我你目前在做什么项目(比如:Web 后端?单元测试?还是对接 Oracle?),我可以给出更具体的建议
到此这篇关于Spring JDBC实战:参数处理与嵌入式数据库的文章就介绍到这了,更多相关Spring JDBC参数处理内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论