⚡ Spring JDBC 完整体系 · 第 3 讲 · Spring JDBC 最小案例
相关开源地址:
📋 前置知识
| 你只需要会 | 你完全不需要会 |
|---|---|
| Java 基础(类、接口、注解、泛型) | MyBatis / Hibernate 任何知识 |
| 基础 SQL(SELECT、INSERT、WHERE) | 连接池原理、事务隔离级别 |
| 第 2 讲:Spring 容器(IoC + DI) | XML 配置、Spring Boot 自动配置 |
📚 全套教程总览(20 讲从零到架构师)
| 篇章 | 涵盖讲次 | 核心目标 |
|---|---|---|
| 开篇筑基 | 第 1-3 讲 | 认知梳理,建立 Spring JDBC 的底层坐标系 |
| API 精讲 | 第 4-7 讲 | 全面掌握 JdbcTemplate + 事务体系 |
| 工具封装 | 第 8-9 讲 | 手搓条件工具 + 代码生成器,从"使用者"变"创造者" |
| 认知升华 | 第 10 讲 | 模板确定性 vs AI 不确定性,建立技术判断力 |
| 高阶扩展 | 第 11-17 讲 | 多数据源、分库分表、分布式事务 |
| 底层观测 | 第 18-19 讲 | SQL 拦截、性能监控、连接池调优 |
| 终极收尾 | 第 20 讲 | 全链路复盘 + 手搓持久层框架 |
一、开篇 · 告别裸 JDBC 的繁琐
第 2 讲已经建立了 Spring 容器的认知------本质上就是一个 Map。这一讲正式进入 Spring JDBC ,将 DataSource 和 JdbcTemplate 注册进容器,开始用 Spring 方式操作数据库。
对比裸 JDBC(第 1 讲)和 Spring JDBC(本讲):
| 对比项 | 裸 JDBC | Spring JDBC |
|---|---|---|
| 连接管理 | 手动 DriverManager.getConnection() |
JdbcTemplate 自动管理 |
| Statement 创建 | 手动 conn.createStatement() |
JdbcTemplate 自动创建 |
| 结果集遍历 | 手动 while(rs.next()) + rs.getString() |
BeanPropertyRowMapper 一行搞定 |
| 资源关闭 | 手动逆序关闭 | JdbcTemplate 自动关闭 |
| SQL 注入风险 | 字符串拼接,风险高 | 预编译占位符 ?,根治 |
| 代码量 | 30+ 行 | 2-3 行 |
二、极简依赖 · spring-context + spring-jdbc 双核驱动
| 组织 | 依赖 | 作用 |
|---|---|---|
| org.springframework | spring-context |
Spring IoC 容器,Bean 管理 |
| org.springframework | spring-jdbc |
JdbcTemplate、数据源抽象、事务能力 |
| com.h2database | h2 |
内存数据库,零安装零配置 |
| org.projectlombok | lombok |
简化 POJO 的 getter / setter |
| ch.qos.logback | logback-classic |
日志输出,调试必备 |
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>[3.3.11,4.0.0)</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
三、核心代码演示
3.1 外部化配置
连接参数抽取到 jdbc.properties,SQL 日志通过 logback 开启,方便观察 JdbcTemplate 执行的 SQL 和参数绑定。
properties
# jdbc.properties
jdbc.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false
jdbc.user=sa
jdbc.password=
xml
<!-- logback.xml -->
<logger name="org.springframework.jdbc" level="DEBUG" />
<logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="trace" />
3.2 JdbcConfig · 手动注册核心 Bean
DataSource 使用 Spring 内置的 DriverManagerDataSource(无连接池,但足够演示原理)。JdbcTemplate 依赖 DataSource 创建,所有配置透明可见。
java
@Configuration
@ComponentScan("com.gzz")
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.url}") private String url;
@Value("${jdbc.user}") private String user;
@Value("${jdbc.password}") private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, user, password);
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
3.3 DataInitializer · 启动自动建表插数
DataSourceInitializer 在容器启动时自动执行 data.sql,完成建表和测试数据插入。业务代码无需手动初始化数据库。
java
@Slf4j
@Configuration
public class DataInitializer {
@Bean
public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDataSource(dataSource);
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(new ClassPathResource("data.sql"));
initializer.setDatabasePopulator(populator);
log.info("初始化脚本执行完成");
return initializer;
}
}
sql
-- data.sql
DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(32),
age INT
);
INSERT INTO t_user(name, age) VALUES ('张三', 10);
INSERT INTO t_user(name, age) VALUES ('李四', 10);
INSERT INTO t_user(name, age) VALUES ('李四光', 20);
INSERT INTO t_user(name, age) VALUES ('王五', 20);
3.4 实体 + DAO · BeanPropertyRowMapper 一行搞定映射
BeanPropertyRowMapper 自动将结果集列名映射到实体类的同名字段,无需手动遍历 ResultSet。
java
@Setter
@Getter
public class User {
private Integer id;
private String name;
private Integer age;
}
java
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<User> queryUsers() {
String sql = "SELECT id, name, age FROM t_user WHERE id > ?";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class), -1);
}
}
id > ?中的?是预编译占位符,传入-1即查询所有id > -1的记录(全部数据)。预编译占位符从根源上杜绝了 SQL 注入。
3.5 Application · 高仿 Spring Boot 启动逻辑
手动创建 AnnotationConfigApplicationContext,传入配置类触发容器启动。@EventListener 在容器完全就绪后执行查询。
java
@Slf4j
@Component
public class Application {
public static void main(String[] args) {
new AnnotationConfigApplicationContext(JdbcConfig.class);
}
@Autowired
private UserDao dao;
@EventListener(ContextRefreshedEvent.class)
public void run() {
log.info("容器完全启动后执行查询:{}", dao.queryUsers());
}
}
四、运行结果
log
[INFO] 初始化脚本执行完成
[DEBUG] SELECT id, name, age FROM t_user WHERE id > -1
[INFO] 容器完全启动后执行查询:[
User(id=1, name=张三, age=10),
User(id=2, name=李四, age=10),
User(id=3, name=李四光, age=20),
User(id=4, name=王五, age=20)
]
五、本讲核心总结
| 能力 | 裸 JDBC(第 1 讲) | Spring JDBC(本讲) |
|---|---|---|
| 连接管理 | 手动 DriverManager.getConnection() |
JdbcTemplate 自动管理 |
| Statement 创建 | 手动 conn.createStatement() |
JdbcTemplate 自动创建 |
| 结果集遍历 | 手动 while(rs.next()) |
BeanPropertyRowMapper 一行搞定 |
| 资源关闭 | 手动逆序关闭 | JdbcTemplate 自动关闭 |
| SQL 注入 | 字符串拼接,风险高 | 预编译占位符,根治 |
| 代码量 | 30+ 行 | 2-3 行 |
关键技术点回顾
| 技术点 | 说明 |
|---|---|
spring-jdbc 依赖 |
提供 JdbcTemplate、数据源抽象、事务能力 |
DriverManagerDataSource |
Spring 内置基础数据源,适合演示 |
DataSourceInitializer |
启动时自动执行 SQL 脚本 |
BeanPropertyRowMapper |
结果集列名 → 实体类同名字段自动映射 |
预编译占位符 ? |
从根源上杜绝 SQL 注入 |
六、下一讲预告
第 4 讲将引入 HikariCP 连接池 和 DataSourceTransactionManager 事务管理器,从"能跑"升级到"生产可用",补齐性能和事务一致性。