前言
工作这么多年了,第一次使用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"
+ ")");
}
注意事项
实际使用中仍有一些需要注意的关键点,遵循这些最佳实践可以避免常见问题并提高代码质量和性能。 关键注意事项包括:
- 始终防范SQL注入
- 合理管理资源和内存使用
- 正确处理和记录异常
- 适当使用事务管理
- 优化批量操作性能
- 提高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
注入等问题。