在 JPA 和 MyBatis 大行其道的今天,JdbcTemplate 依然有其独特的价值。本文将系统讲解 JdbcTemplate 的核心 API、CRUD 实战、批量操作、事务管理,以及如何结合 Druid 监控分析 SQL 性能。
一、为什么还要学 JdbcTemplate?
1️⃣ JdbcTemplate 的定位
| 方案 | 特点 | 适用场景 |
|---|---|---|
| JdbcTemplate | 轻量级、SQL 可控、学习成本低 | 简单 CRUD、报表查询、遗留系统 |
| JPA / Hibernate | 面向对象、自动生成 SQL | 领域模型复杂、快速开发 |
| MyBatis | SQL 与代码分离、灵活 | 复杂 SQL、互联网项目 |
📌 JdbcTemplate 的优势:
- ✅ 零学习成本(只要会 SQL)
- ✅ 性能极高(无反射、无代理)
- ✅ SQL 完全可控
- ✅ 与 Druid 监控完美结合
2️⃣ Spring Boot 4 中的 JdbcTemplate
Spring Boot 4 自动配置 了 JdbcTemplate:
@Autowired
private JdbcTemplate jdbcTemplate; // 直接可用
二、核心 API 速览
1️⃣ 更新操作(INSERT / UPDATE / DELETE)
int update(String sql, Object... args)
2️⃣ 查询操作
// 返回单个对象
T queryForObject(String sql, RowMapper<T> rowMapper, Object... args)
// 返回 List
List<T> query(String sql, RowMapper<T> rowMapper, Object... args)
// 返回 Map
Map<String, Object> queryForMap(String sql, Object... args)
// 返回基础类型
T queryForObject(String sql, Class<T> requiredType, Object... args)
3️⃣ RowMapper:结果集映射的核心
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
三、CRUD 实战(用户管理示例)
1️⃣ 建表
CREATE TABLE user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(100),
age INT,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
2️⃣ 实体类
public record User(
Long id,
String username,
String email,
Integer age,
LocalDateTime createTime
) {}
3️⃣ RowMapper 实现
public class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return new User(
rs.getLong("id"),
rs.getString("username"),
rs.getString("email"),
rs.getInt("age"),
rs.getTimestamp("create_time").toLocalDateTime()
);
}
}
4️⃣ DAO 层实现
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
private final RowMapper<User> rowMapper = new UserRowMapper();
// 插入
public int insert(User user) {
String sql = "INSERT INTO user (username, email, age) VALUES (?, ?, ?)";
return jdbcTemplate.update(sql,
user.username(),
user.email(),
user.age());
}
// 根据 ID 查询
public User findById(Long id) {
String sql = "SELECT * FROM user WHERE id = ?";
return jdbcTemplate.queryForObject(sql, rowMapper, id);
}
// 查询所有
public List<User> findAll() {
String sql = "SELECT * FROM user";
return jdbcTemplate.query(sql, rowMapper);
}
// 更新
public int update(User user) {
String sql = "UPDATE user SET username = ?, email = ?, age = ? WHERE id = ?";
return jdbcTemplate.update(sql,
user.username(),
user.email(),
user.age(),
user.id());
}
// 删除
public int deleteById(Long id) {
String sql = "DELETE FROM user WHERE id = ?";
return jdbcTemplate.update(sql, id);
}
}
5️⃣ Service 层
@Service
public class UserService {
@Autowired
private UserDao userDao;
public Result<User> create(User user) {
userDao.insert(user);
return Result.success(user);
}
public Result<User> getById(Long id) {
return Result.success(userDao.findById(id));
}
public Result<List<User>> list() {
return Result.success(userDao.findAll());
}
public Result<?> update(User user) {
userDao.update(user);
return Result.success();
}
public Result<?> delete(Long id) {
userDao.deleteById(id);
return Result.success();
}
}
四、批量操作(性能关键)
1️⃣ 批量插入
public int[] batchInsert(List<User> users) {
String sql = "INSERT INTO user (username, email, age) VALUES (?, ?, ?)";
return jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
User user = users.get(i);
ps.setString(1, user.username());
ps.setString(2, user.email());
ps.setInt(3, user.age());
}
@Override
public int getBatchSize() {
return users.size();
}
});
}
📌 批量操作性能提升 10 倍以上!
2️⃣ 使用 BatchSqlParameterSource(更优雅)
public int[] batchInsertWithNamedParam(List<User> users) {
String sql = "INSERT INTO user (username, email, age) VALUES (:username, :email, :age)";
SqlParameterSource[] params = SqlParameterSourceUtils.createBatch(users);
return namedParameterJdbcTemplate.batchUpdate(sql, params);
}
五、NamedParameterJdbcTemplate(推荐)
1️⃣ 为什么用命名参数?
❌ 传统方式:
jdbcTemplate.update(sql, id, name, email, age); // 参数顺序不能错
✅ 命名参数:
namedParameterJdbcTemplate.update(
"INSERT INTO user (username, email, age) VALUES (:username, :email, :age)",
Map.of("username", "zhangsan", "email", "zs@test.com", "age", 18)
);
2️⃣ 配置 NamedParameterJdbcTemplate
@Configuration
public class JdbcConfig {
@Bean
public NamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
}
3️⃣ 实战示例
@Repository
public class UserDao {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
public User findById(Long id) {
String sql = "SELECT * FROM user WHERE id = :id";
return jdbcTemplate.queryForObject(sql, Map.of("id", id), new UserRowMapper());
}
public int update(User user) {
String sql = """
UPDATE user
SET username = :username,
email = :email,
age = :age
WHERE id = :id
""";
return jdbcTemplate.update(sql, new BeanPropertySqlParameterSource(user));
}
}
✅ 代码更清晰、参数更安全
六、事务管理(生产级必会)
1️⃣ 声明式事务
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public Result<?> transfer(Long fromId, Long toId, BigDecimal amount) {
User from = userDao.findById(fromId);
User to = userDao.findById(toId);
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
userDao.update(from);
userDao.update(to);
return Result.success();
}
}
2️⃣ 事务传播行为与隔离级别
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
rollbackFor = Exception.class,
timeout = 30
)
public void complexBusiness() {
// 业务逻辑
}
3️⃣ 编程式事务(高级)
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public Result<?> batchCreate(List<User> users) {
return transactionTemplate.execute(status -> {
try {
users.forEach(userDao::insert);
return Result.success();
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
}
}
七、与 Druid 监控结合(实战价值极高)
1️⃣ 查看 SQL 执行统计
访问 Druid 监控页面 → SQL 监控:
- 执行次数
- 执行时间(平均、最慢)
- 慢 SQL 列表
2️⃣ 定位慢 SQL
spring:
datasource:
druid:
stat:
slow-sql-millis: 1000 # 超过 1 秒的 SQL 记录为慢 SQL
merge-sql: true
3️⃣ 实战案例:优化慢 SQL
// ❌ 慢 SQL:全表扫描
jdbcTemplate.query("SELECT * FROM user WHERE username LIKE '%zhang%'", rowMapper);
// ✅ 优化:加索引 + 前缀匹配
jdbcTemplate.query("SELECT * FROM user WHERE username LIKE ?", rowMapper, "zhang%");
八、Spring Boot 4 中的新变化
- 对
java.time支持更完善 - 与虚拟线程(Virtual Threads)兼容
- 自动配置更智能
九、常见坑位总结
❌ 坑1:RowMapper 中字段名写错
✅ 解决:使用 rs.findColumn() 检查
❌ 坑2:queryForObject 查不到数据抛异常
✅ 解决:用 query() 返回 List,判断是否为空
❌ 坑3:批量操作性能差
✅ 解决:使用 batchUpdate
❌ 坑4:事务不生效
✅ 解决:检查 @Transactional 是否在 public 方法上、是否同类调用
十、本篇总结
- JdbcTemplate 是轻量级、高性能的数据库操作方案
- RowMapper 是结果集映射的核心
- NamedParameterJdbcTemplate 比传统方式更优雅
- 批量操作用 batchUpdate,性能提升 10 倍+
- 事务管理用 @Transactional,生产环境必须配置