为什么要用JdbcTemplate

首页 编程分享 PHP丨JAVA丨OTHER 正文

提前退休的java猿 转载 编程分享 2025-05-10 22:03:37

简介 工作这么多年了,第一次使用JdbcTemplate。使用的时候当然要全方位的了解它。了解它的使用方式,使用场景以及注意事项等相关知识,今天就和大家一起学习一下吧😁


前言

工作这么多年了,第一次使用JdbcTemplate。使用的时候当然要全方位的了解它。了解它的使用方式,使用场景以及注意事项等相关知识,今天就和大家一起学习一下吧😁

JdbcTemplate 核心知识

基础概念及使用场景

基础概念

JdbcTemplate是Spring框架核心包的一部分,位于spring-jdbc模块中,因此通常不用我们再额外引入依赖,直接就可以。它封装了JDBC的核心流程,消除了传统的JDBC开发中大量重复的代码。当然最核心的就是直接在代码中执行SQL

// 插入数据
jdbcTemplate.update("INSERT INTO users(name, email) VALUES(?, ?)", "张三", "zhangsan@example.com");

// 查询单条记录
User user = jdbcTemplate.queryForObject(
    "SELECT * FROM users WHERE id = ?", 
    new Object[]{1}, 
    new BeanPropertyRowMapper<>(User.class));

因此它和 mybatis 这些 orm 框架相比使用就更加的简单了,效率也更高。但是在开发中我们不会去用它来写业务代码,毕竟规范通常来说更加的重要。

使用场景

它的使用场景在哪些地方呢,以本次工作场景为例。这次的业务需求 需要实现动态建表的功能,以及在插入数据的时候,表名 和 字段都是动态的。利用orm框架去处理ddl语句显示不是很好,以及表名和字段都是需要计算才知道显然在java代码中拼接好SQL之后直接执行更好。

所以在执行ddl语句,存储过程,以及SQL 必须在程序中进行拼接的场景,用JdbcTemplate更为合适

在实际项目中,JdbcTemplate常与JPA或MyBatis等ORM框架混合使用。ORM框架处理常规业务逻辑,而JdbcTemplate处理特殊场景如ddl、复杂查询、批量操作等。这种混合模式能兼顾开发效率和执行性能。(并且在springboot工程中通常不需要在单独引入依赖,直接使用就可以了)

基本使用语法

1. 基本配置

在Spring Boot中,只需在application.properties中配置数据源,Spring会自动创建JdbcTemplate bean:

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

导入bean就可以直接使用了

@Resource
private JdbcTemplate jdbcTemplate;

2. 执行查询操作

JdbcTemplate提供了多种查询方法,以下是一些常用示例:

查询单个值:

// 查询用户总数
int count = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM users", Integer.class);

查询单条记录:

// 使用RowMapper将结果集映射为对象
User user = jdbcTemplate.queryForObject(
    "SELECT * FROM users WHERE id = ?", 
    new Object[]{1}, 
    new BeanPropertyRowMapper<>(User.class));

查询多条记录:

// 查询所有用户
List<User> users = jdbcTemplate.query(
    "SELECT * FROM users", 
    new BeanPropertyRowMapper<>(User.class));

3. 执行更新操作

插入数据:

jdbcTemplate.update(
    "INSERT INTO users(name, email) VALUES(?, ?)", 
    "张三", "zhangsan@example.com");

更新数据:

int rowsAffected = jdbcTemplate.update(
    "UPDATE users SET email = ? WHERE id = ?", 
    "newemail@example.com", 1);

删除数据:

int rowsDeleted = jdbcTemplate.update(
    "DELETE FROM users WHERE id = ?", 1);

4. 批量操作

JdbcTemplate支持高效的批量操作:

jdbcTemplate.batchUpdate(
    "INSERT INTO users(name, email) VALUES(?, ?)",
    new BatchPreparedStatementSetter() {
        @Override
        public void setValues(PreparedStatement ps, int i) throws SQLException {
            ps.setString(1, "User" + i);
            ps.setString(2, "user" + i + "@example.com");
        }
        
        @Override
        public int getBatchSize() {
            return 10;
        }
    });

5. 命名参数支持

使用NamedParameterJdbcTemplate可以更清晰地处理参数:

NamedParameterJdbcTemplate namedTemplate = 
    new NamedParameterJdbcTemplate(dataSource);

Map<String, Object> params = new HashMap<>();
params.put("id", 1);
params.put("email", "newemail@example.com");

namedTemplate.update(
    "UPDATE users SET email = :email WHERE id = :id", 
    params);

6.执行DDL

直接把原生的SQL语句执行就行了,比如建表操作:

public void createUserTable() {
    jdbcTemplate.execute("CREATE TABLE users ("
        + "id BIGINT PRIMARY KEY AUTO_INCREMENT,"
        + "username VARCHAR(50) NOT NULL UNIQUE,"
        + "password VARCHAR(100) NOT NULL,"
        + "email VARCHAR(100),"
        + "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,"
        + "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
        + ")");
}

注意事项

实际使用中仍有一些需要注意的关键点,遵循这些最佳实践可以避免常见问题并提高代码质量和性能。 关键注意事项包括:

  1. 始终防范SQL注入
  2. 合理管理资源和内存使用
  3. 正确处理和记录异常
  4. 适当使用事务管理
  5. 优化批量操作性能
  6. 提高SQL可维护性

1. SQL注入防护

问题:直接拼接SQL字符串会导致SQL注入风险

正确做法

  • 始终使用参数化查询
  • 绝对不要直接拼接用户输入到SQL中
// ❌ 错误做法(有SQL注入风险)
String sql = "SELECT * FROM users WHERE name = '" + name + "'";
jdbcTemplate.query(sql, rowMapper);

// ✅ 正确做法(使用参数占位符)
jdbcTemplate.query("SELECT * FROM users WHERE name = ?", new Object[]{name}, rowMapper);

2. 资源管理

问题:虽然JdbcTemplate会关闭连接,但某些资源仍需注意

注意事项

  • 对于大型结果集,使用RowCallbackHandler而非RowMapper进行流式处理
  • 使用JdbcTemplate.query()方法时,确保结果集不会过大导致内存溢出
// 处理大型结果集的推荐方式
jdbcTemplate.query("SELECT * FROM large_table", 
    rs -> {
        // 逐行处理,不一次性加载所有数据
        while (rs.next()) {
            processRow(rs);
        }
    });

3. 异常处理

问题:JdbcTemplate会将SQLException转换为DataAccessException

正确处理

  • 捕获特定的DataAccessException子类而非泛化的异常
  • 记录完整的异常信息以便排查问题
try {
    jdbcTemplate.update("UPDATE accounts SET balance = ? WHERE id = ?", amount, accountId);
} catch (DuplicateKeyException e) {
    // 处理唯一键冲突
    log.error("Duplicate key violation", e);
    throw new BusinessException("Account already exists");
} catch (DataAccessException e) {
    // 处理其他数据库异常
    log.error("Database access error", e);
    throw new ServiceException("Database operation failed");
}

4. 事务管理

问题:默认情况下每个操作是独立事务;

注意:DDL语句通常是自动提交的,不受常规事务管理控制

最佳实践

  • 对需要原子性的一组操作使用@Transactional注解
  • 注意事务传播行为和隔离级别设置
@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
    jdbcTemplate.update("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, fromId);
    jdbcTemplate.update("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, toId);
}

5. 性能优化

注意事项

  • 批量操作使用batchUpdate而非循环执行单条更新
  • 重用预编译语句(JdbcTemplate内部已优化)
  • 考虑使用NamedParameterJdbcTemplate提高复杂SQL可读性
// 批量插入优化示例
jdbcTemplate.batchUpdate(
    "INSERT INTO users (name, email) VALUES (?, ?)",
    new BatchPreparedStatementSetter() {
        public void setValues(PreparedStatement ps, int i) throws SQLException {
            User user = users.get(i);
            ps.setString(1, user.getName());
            ps.setString(2, user.getEmail());
        }
        public int getBatchSize() {
            return users.size();
        }
    });

6. SQL可维护性

问题SQL硬编码在Java类中难以维护,这也是我们平时做业务开发,不用使用jdbcTemplate重要原因之一

解决方案

  • 将SQL语句提取到常量或配置文件中
  • 使用Spring的@Sql注解管理测试数据
  • 考虑使用SQL构建工具如QueryDSL
// 将SQL提取为常量
private static final String FIND_USER_BY_ID = 
    "SELECT id, name, email FROM users WHERE id = ?";

public User findById(Long id) {
    return jdbcTemplate.queryForObject(
        FIND_USER_BY_ID,
        new Object[]{id},
        new BeanPropertyRowMapper<>(User.class));
}

总结

JdbcTemplate在工作中还是很少用到的,业务代码还是用orm。特殊的场景比如执行ddl、存储过程,或者orm不方便操作的可以考虑使用jdbcTemplate执行这部分语句 。当然在使用过程的注意事项还是要了解的,比如防止SQL注入等问题。

转载链接:https://juejin.cn/post/7501528667958313014


Tags:


本篇评论 —— 揽流光,涤眉霜,清露烈酒一口话苍茫。


    声明:参照站内规则,不文明言论将会删除,谢谢合作。


      最新评论




ABOUT ME

Blogger:袅袅牧童 | Arkin

Ido:PHP攻城狮

WeChat:nnmutong

Email:nnmutong@icloud.com

标签云