spring07-JdbcTemplate操作数据库
一、JdbcTemplate 是什么?
JdbcTemplate 是 Spring 提供的一个用来简化 JDBC 操作的工具类。
它属于 Spring 的 spring-jdbc
模块,封装了对原生 JDBC 的操作流程,如:
-
获取连接
-
创建
Statement
-
执行 SQL
-
处理结果集
-
关闭资源等
你只需要关心:
“我要执行什么 SQL、传什么参数、返回什么类型”,剩下的繁琐操作都交给它。
二、JdbcTemplate 的作用
简而言之,它是用来操作数据库的,支持:
操作类型 | 方法 | 举例 |
---|---|---|
增删改 | update() | 插入、删除、更新 |
查询(返回一个对象) | queryForObject() | 查总数、查单个字段 |
查询(返回多个对象) | query() | 查表中的所有记录 |
批处理 | batchUpdate() | 一次执行多条插入语句 |
三、为什么要用 JdbcTemplate?
我们先来看原生 JDBC 写法(写一条 INSERT):
Connection conn = null;
PreparedStatement stmt = null;
try {conn = DriverManager.getConnection(...);stmt = conn.prepareStatement("INSERT INTO user(name, age) VALUES(?, ?)");stmt.setString(1, "Tom");stmt.setInt(2, 20);stmt.executeUpdate();
} catch (Exception e) {...
} finally {stmt.close();conn.close();
}
问题:太繁琐了!大量重复的样板代码,还容易忘关连接、出错。
使用 JdbcTemplate:
jdbcTemplate.update("INSERT INTO user(name, age) VALUES(?, ?)", "Tom", 20);
仅一行代码,干净、清爽、安全、性能好!
四、JdbcTemplate 的使用步骤
使用:Spring 开发规范的三层架构写法。
层级 | 类 | 说明 |
---|---|---|
Dao 接口 | UserDao | 声明操作方法 |
Dao 实现类 | UserDaoImpl | 使用 JdbcTemplate 实现数据库操作 |
Service 类 | UserService | 通过 @Autowired 注入 DAO,实现业务逻辑 |
依赖注入 | Spring 容器 | 自动注入 UserDaoImpl 给 UserDao 接口 |
步骤一:添加相关依赖
<!-- 数据库连接池 --><!-- Druid 依赖 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.8</version></dependency><!-- MySQL JDBC 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version></dependency><!-- JdbcTemplate 和相关支持 --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.24</version></dependency>
步骤二:
1、在xml配置文件中配置数据源(DataSource)
2、在xml配置文件中创建 JdbcTemplate
Bean
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><!-- 基本连接信息 --><property name="driverClassName" value="${db.driverClassName}" /><property name="url" value="${db.url}" /><property name="username" value="${db.username}" /><property name="password" value="${db.password}" /></bean><!-- 配置 JdbcTemplate --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean>
步骤三:创建DAO 层接口和实现类
public interface UserDao {public void addUser(User user);public List<User> findAll();void updateUser(User user);void deleteUser(int id);}
@Repository
public class UserDaoImpl implements UserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic void addUser(User user) {// 创建sql语句String sql = "INSERT INTO tb_user VALUES(?,?,?,?,?)";// 实现jdbcTemplate调用update方法,新增一条数据jdbcTemplate.update(sql, user.getId(), user.getUserName(), user.getPassword(), user.getGender(), user.getAddr());}@Overridepublic List<User> findAll() {String sql = "SELECT * FROM tb_user";return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));}@Overridepublic void updateUser(User user) {String sql = "update tb_user set user_name=?, password=? where id=?";Object[] args = {user.getUserName(), user.getPassword(), user.getId()};int update = jdbcTemplate.update(sql, args);System.out.println("成功更新:"+update + "条");}@Overridepublic void deleteUser(int id) {String sql = "delete from tb_user where id=?";int delete = jdbcTemplate.update(sql, id);System.out.println("成功删除:"+ delete + "条");}
}
【注意】:
在dao层实现类中注入:JdbcTemplate
步骤四、编写测试类
@Testpublic void test01(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = context.getBean("userServiceTemp", UserService.class);User user = new User();user.setId(4);user.setUserName("orm");user.setPassword("orm123");user.setGender("女");user.setAddr("清迈");userService.addUser(user);}@Testpublic void test02(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = context.getBean("userServiceTemp", UserService.class);userService.findAll();}@Testpublic void testUpdate(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = context.getBean("userServiceTemp", UserService.class);User user = new User();user.setId(4);user.setUserName("orm kp");user.setPassword("orm123456");userService.updateUser(user);}@Testpublic void testDelete(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = context.getBean("userServiceTemp", UserService.class);userService.deleteUser(4);}
五、JdbcTemplate 的优点总结
优点 | 说明 |
---|---|
简化代码 | 不用写连接获取、释放、异常处理 |
安全 | 自动防 SQL 注入(参数绑定) |
性能好 | 配合连接池使用 |
易维护 | 写法规范,容易阅读和维护 |
易集成 | 与 Spring 完美集成,支持事务管理等 |
一句话总结:
JdbcTemplate
是 Spring 提供的用于简化数据库访问的工具类,它封装了传统 JDBC 的繁琐步骤,让你更专注于业务逻辑,写出简洁、高效、安全的数据库代码。
六、面向接口编程
UserService里面注入的是接口:UserDao,而不是实现类UserDaoImpl,这正体现了面向接口编程在 Spring 架构中的核心思想之一。
这个问题问得非常好!这正体现了面向接口编程在 Spring 架构中的核心思想之一。
6-1、为什么注入的是接口而不是实现类?
因为这是 解耦、灵活、可替换、易测试 的关键做法。
1. 面向接口编程(OOP 原则)
@Autowired
private UserDao userDao; // 接口
虽然写的是接口,但 Spring 会在启动时根据实际情况注入接口的实现类(如
UserDaoImpl
)。这是 OOP 里的“面向接口而不是实现编程”原则。
好处:
-
不依赖具体实现类,代码更灵活
-
后期如果你要换成
MyBatisUserDaoImpl
或JpaUserDaoImpl
,不用改 Service 代码 -
容易进行单元测试(可以 mock 接口)
2. 实现解耦(降低模块间依赖)
如果写成:
@Autowired
private UserDaoImpl userDao;
这意味着:
-
UserService
直接依赖UserDaoImpl
-
如果
UserDaoImpl
替换成了别的实现类,你需要修改 Service 层的代码 -
这违反了 开闭原则(对扩展开放,对修改关闭)
而使用接口:
@Autowired
private UserDao userDao;
就可以轻松切换实现类,只要实现类使用了 @Repository
并被 Spring 扫描到,就会被自动注入。
3. 依赖注入机制 + 多实现支持
Spring 会在启动时:
-
找到
UserDao
的实现类(如UserDaoImpl
) -
自动创建
UserDaoImpl
对象并注入到UserService.userDao
如果有多个实现类,比如:
@Repository
public class JdbcUserDaoImpl implements UserDao {}@Repository
public class MyBatisUserDaoImpl implements UserDao {}
你可以通过 @Qualifier
指定注入哪个:
@Autowired
@Qualifier("jdbcUserDaoImpl")
private UserDao userDao;
6-2、总结对比
写法 | 灵活性 | 耦合度 | 可测试性 | 推荐程度 |
---|---|---|---|---|
注入接口 (UserDao ) | 高 | 低 | 容易 Mock | ✅✅✅✅✅ |
注入实现类 (UserDaoImpl ) | 低 | 高 | 不好替换 | ❌ |
你说得非常对 ✅,现在在实际企业项目中确实更常用 MyBatis 或 JPA,而不是直接使用 JdbcTemplate
,原因我们可以从以下几个方面来讲解:
七、JdbcTemplate 在 Spring 中的定位
JdbcTemplate
是 Spring 提供的最基础的数据库操作工具,它封装了原生 JDBC,让你不再手写繁琐的连接/释放代码。
但它本质上还是“手写 SQL + 数据映射”,工作量仍然不小,不适合大规模开发。
7-1、现实项目中为什么更常用 MyBatis 或 JPA?
1. MyBatis(半自动 ORM,最流行)
开发者写 SQL,MyBatis 自动将 SQL 映射成 Java 对象。
优势:
-
SQL 可控(性能调优好)
-
XML 或注解方式灵活配置
-
支持动态 SQL、复杂关联、分页
-
Spring Boot 中集成非常成熟
-
有插件生态(分页、缓存等)
适合大多数企业项目,特别是互联网、电商、政务等项目。
2. JPA / Hibernate(全自动 ORM)
不写 SQL,直接通过实体类操作数据库。
优势:
-
开发快,效率高
-
全自动建表、查询、更新
-
支持复杂关联(@OneToMany、@ManyToOne)
劣势:
-
性能调优复杂
-
对复杂 SQL 支持不如 MyBatis
-
学习曲线相对更陡
适合快速开发型项目、后台系统、Spring Data 项目。
3. JdbcTemplate 的不足
项目 | MyBatis | JPA | JdbcTemplate |
---|---|---|---|
SQL 控制 | 👍 完全手写 | 👎 自动生成 | 👍 手写 |
映射能力 | 👍 自定义、灵活 | 👍 注解/映射多样 | 👎 需写 RowMapper |
关联关系支持 | 👍 多表连接容易 | 👍 注解即可 | 👎 全手写 |
易用性 | 中等(写SQL) | 简单(不写SQL) | 低(写很多模板代码) |
企业项目常用度 | ✅✅✅✅✅ | ✅✅✅ | ✅(较少) |
7-2、Spring Boot 项目的主流选择
在 Spring Boot 中,现在主流做法是:
需求 | 推荐方案 |
---|---|
想要灵活写 SQL、自定义性能 | MyBatis + PageHelper |
想要开发快、少写代码 | Spring Data JPA |
非常轻量、只有几条 SQL | JdbcTemplate |
7-3、什么时候还是会用 JdbcTemplate?
-
编写工具类操作数据库(如清理任务)
-
对性能要求极高且 SQL 简单时
-
替代原生 JDBC 写法
-
在框架内部使用(Spring 自身或某些底层库)
7-4、结论
JdbcTemplate 是 Spring 非常重要的数据库工具类,但在现代 Spring Boot 项目中更常用 MyBatis 或 JPA,前者兼具灵活与高性能,后者开发效率高。
八、JdbcTemplate 查询常用三种方式
查询类型 | 方法 | 示例用途 |
---|---|---|
1️⃣ 返回单个值 | queryForObject() | 总记录数、最大值、某个字段等 |
2️⃣ 返回单个对象 | queryForObject() + RowMapper | 查询一个 User 对象 |
3️⃣ 返回对象集合 | query() + RowMapper | 查询所有 User 对象 |
前提准备(假设有如下用户表)
CREATE TABLE user (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50),age INT
);
Java 对应实体类:
public class User {private Integer id;private String name;private Integer age;// getter / setter
}
8-1、查询某个值:queryForObject()
示例 1:查询总人数
String sql = "SELECT COUNT(*) FROM user";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println("总人数:" + count);
示例 2:查询某个名字的年龄
String sql = "SELECT age FROM user WHERE name = ?";
Integer age = jdbcTemplate.queryForObject(sql, Integer.class, "Tom");
⚠️ 注意:
如果查询结果为 null 会抛异常(如找不到记录)
如果返回多行也会抛异常
8-2、查询单个对象:queryForObject()
+ RowMapper
示例:根据 id 查询一个用户对象
String sql = "SELECT * FROM user WHERE id = ?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 1);System.out.println(user.getName());
BeanPropertyRowMapper
会自动将查询结果按列名 → Java 属性名进行映射(支持下划线转驼峰)。
8-3、查询对象集合:query()
+ RowMapper
示例:查询所有用户
String sql = "SELECT * FROM user";
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));for (User user : userList) {System.out.println(user.getName() + " - " + user.getAge());
}
8-4、总结对比
类型 | 方法 | 说明 |
---|---|---|
返回一个值 | queryForObject(String sql, Class<T> requiredType, Object... args) | 查询数量、某字段 |
返回单个对象 | queryForObject(String sql, RowMapper<T> rowMapper, Object... args) | 一行数据映射为对象 |
返回对象集合 | query(String sql, RowMapper<T> rowMapper) | 多行数据映射为 List |
九、批量操作的使用方法
JdbcTemplate
完全支持批量操作(增删改),而且它的效率比单条update
循环高很多,常用于批量插入或删除数据的场景。
使用:
jdbcTemplate.batchUpdate(String sql, List<Object[]> batchArgs)
它适用于:
-
批量插入 INSERT
-
批量更新 UPDATE
-
批量删除 DELETE
9-1、方法参数说明
public int[] batchUpdate(String sql, List<Object[]> batchArgs)
参数 | 说明 |
---|---|
sql | SQL 语句,带 ? 占位符 |
batchArgs | 每一行数据对应一个 Object[] ,多个对象构成 List |
返回值是 int[]
,表示每条 SQL 执行影响的行数。
假设我们有如下 user
表:
CREATE TABLE user (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50),age INT
);
9-2、批量新增(INSERT)
String sql = "INSERT INTO user(name, age) VALUES(?, ?)";List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{"Tom", 20});
batchArgs.add(new Object[]{"Jerry", 22});
batchArgs.add(new Object[]{"Alice", 18});jdbcTemplate.batchUpdate(sql, batchArgs);
9-3、批量更新(UPDATE)
String sql = "UPDATE user SET age = ? WHERE name = ?";List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{25, "Tom"});
batchArgs.add(new Object[]{23, "Jerry"});
batchArgs.add(new Object[]{19, "Alice"});jdbcTemplate.batchUpdate(sql, batchArgs);
9-4、批量删除(DELETE)
String sql = "DELETE FROM user WHERE name = ?";List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{"Tom"});
batchArgs.add(new Object[]{"Jerry"});jdbcTemplate.batchUpdate(sql, batchArgs);
9-5、注意事项
项目 | 建议 |
---|---|
批量大小 | 控制在 500~1000 条一批,避免 SQL 太大 |
数据顺序 | 保持和 SQL 中 ? 顺序一致 |
性能 | 一般比单条 update 快 3~10 倍 |
返回值 | int[] 表示每条语句影响行数,可选判断成功与否 |