数据访问(一):JdbcTemplate
依赖与驱动
spring-boot-starter-jdbc 自带 HikariCP 连接池,并为单数据源自动配置 DataSource 与 JdbcTemplate。还要按数据库加驱动,本教程用 H2 内存库便于直接运行:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
生产换 MySQL 用 com.mysql:mysql-connector-j、PostgreSQL 用 org.postgresql:postgresql(版本由 Boot 依赖管理锁定)。
数据源配置
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL
spring.datasource.username=sa
spring.datasource.password=
driver-class-name 一般可省略,Boot 按 URL 推断。连接池默认 HikariCP,池参数用 spring.datasource.hikari.* 调整,完整清单以官方文档为准。
建表
把 schema.sql 放进 src/main/resources,默认对嵌入式数据库自动执行(非嵌入式库需 spring.sql.init.mode=always,细节以官方文档为准):
CREATE TABLE IF NOT EXISTS t_user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age INT
);
CRUD 示例
@Repository
public class UserDao {
private final JdbcTemplate jdbc;
public UserDao(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
public int insert(String name, int age) {
return jdbc.update("insert into t_user(name, age) values (?, ?)", name, age);
}
public int updateAge(Long id, int age) {
return jdbc.update("update t_user set age = ? where id = ?", age, id);
}
public int deleteById(Long id) {
return jdbc.update("delete from t_user where id = ?", id);
}
public User findById(Long id) {
return jdbc.queryForObject("select * from t_user where id = ?",
(rs, n) -> new User(rs.getLong("id"), rs.getString("name"), rs.getInt("age")), id);
}
public List<User> findByName(String name) {
return jdbc.query("select * from t_user where name = ?",
(rs, n) -> new User(rs.getLong("id"), rs.getString("name"), rs.getInt("age")), name);
}
}
要点:
update执行增删改,返回受影响行数;query/queryForObject需要RowMapper,可用 lambda(rs, rowNum) -> ...手写映射;- 单行查询可能查不到时,用
query(...).stream().findFirst()代替queryForObject,避免EmptyResultDataAccessException。
命名参数
SQL 里用 :name 而不是 ?,配合 Boot 同样自动配置的 NamedParameterJdbcTemplate:
named.query("select * from t_user where name = :name and age >= :minAge",
Map.of("name", name, "minAge", 18),
(rs, n) -> new User(rs.getLong("id"), rs.getString("name"), rs.getInt("age")));
参数多、可读性要求高时推荐用它。实体字段与命名参数同名时,也可用 BeanPropertySqlParameterSource 直接把对象属性当参数。
JdbcClient:更现代的写法
Spring Framework 6.1 起提供流式 API JdbcClient(Boot 3.2 起为单数据源自动提供,4.x 沿用,以官方文档为准):
User user = jdbcClient.sql("select * from t_user where id = :id")
.param("id", id)
.query((rs, n) -> new User(rs.getLong("id"), rs.getString("name"), rs.getInt("age")))
.single();
小结
JdbcTemplate 是 SQL 直写、结果手工映射的传统方案,依赖少、可控性强。需要对象关系映射与大量样板 CRUD 时见 15 章 Spring Data JPA;需要 SQL 完全可控的复杂查询时见 16 章 MyBatis。