Spring Boot 4 深度解析,JdbcTemplate 实战——轻量级数据库操作方案

在 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 方法上、是否同类调用


十、本篇总结

  1. JdbcTemplate 是轻量级、高性能的数据库操作方案
  2. RowMapper 是结果集映射的核心
  3. NamedParameterJdbcTemplate 比传统方式更优雅
  4. 批量操作用 batchUpdate,性能提升 10 倍+
  5. 事务管理用 @Transactional,生产环境必须配置
相关推荐
李兆龙的博客29 分钟前
问津集 #12:Calcspar:相同 Paid IOPS 下的 RocksDB 尾延迟优化
数据库
智购科技自动售卖机厂家29 分钟前
2026自动售货机云端数据备份策略:从全量备份到增量恢复的工程实践~YH
数据库·stm32·嵌入式硬件·物联网·架构·零售
mqiqe1 小时前
AgentScope Java 2.0 技能仓库(Skill Repository)完全实战指南
java·开发语言·elasticsearch
李昊哲小课1 小时前
Spring Boot 4 旅游主题实战教程 阶段二:Web 开发基础
前端·spring boot·旅游
Mr数据杨1 小时前
【Codex】用学生支持周报模块跟踪个性化支持进展
android·java·javascript·django·codex·项目开发
JavaPub-rodert1 小时前
Apache Jena 详解:Java 开发者如何真正搭起一套知识图谱与语义服务
java·apache·知识图谱
笔头子1 小时前
【Caché】数据库基础操作
数据库
上火的金鱼妹1 小时前
redis集群部署与连接
数据库·redis·缓存
lhldsg1 小时前
线上住宿预约系统开发实战:从需求分析到接口设计
java·大数据·小程序·需求分析