抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文学习了如何在Spring中使用JdbcTemplate操作数据。

环境

Windows 10 企业版 LTSC 21H2
MySQL 5.7.40
Java 1.8
Tomcat 8.5.50
Maven 3.6.3
Spring 5.2.25.RELEASE

1 概述

JdbcTemplate是Spring框架提供的一个核心类,用于简化JDBC操作。它封装了JDBC访问数据库的模板代码,让开发者能够专注于业务逻辑。

JdbcTemplate的主要优势:

  • 简化JDBC代码,减少模板代码
  • 自动处理资源的获取和释放
  • 统一异常处理,将SQLException异常转换为Spring的DataAccessException异常
  • 提供回调接口,便于自定义数据处理逻辑

2 环境准备

2.1 数据

创建数据库表:

mysql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE DATABASE IF NOT EXISTS test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE test;

CREATE TABLE IF NOT EXISTS user (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL,
email VARCHAR(100)
);

INSERT INTO user (name, age, email) VALUES
('张三', 25, 'zhangsan@example.com'),
('李四', 30, 'lisi@example.com'),
('王五', 28, 'wangwu@example.com');

2.2 依赖

添加依赖:

pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- Spring Context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.25.RELEASE</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.25.RELEASE</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>

2.3 实体类

创建实体类:

java
1
2
3
4
5
6
7
public class User {
private Integer id;
private String name;
private Integer age;
private String email;
// ...
}

3 配置方式

3.1 XML配置

创建服务类:

java
1
2
3
4
5
6
7
public class UserService {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 业务方法
}

创建配置文件:

spring.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建UserService对象 -->
<bean id="userService" class="com.example.service.UserService">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>

<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false&amp;serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<!-- 配置连接池 -->
<property name="initialSize" value="10"/>
<property name="minIdle" value="10"/>
<property name="maxActive" value="20"/>
<property name="maxWait" value="60000"/>
</bean>

<!-- 配置数据模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>

创建启动类:

java
1
2
3
4
5
6
7
8
9
10
11
public class DemoApplication {
public static void main(String[] args) {
// 加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 获取UserService对象
UserService userService = context.getBean(UserService.class);
// 调用业务方法
// 关闭容器
context.close();
}
}

3.2 注解配置

修改服务类:

java
1
2
3
4
5
6
7
8
9
@Service
public class UserService {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 业务方法
}

创建配置类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Configuration
@ComponentScan("com.example")
public class DemoConfig {
// 配置数据连接
@Bean
public DataSource dataSource() {
// 配置数据源
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=GMT%2B8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
// 连接池配置
dataSource.setInitialSize(10);
dataSource.setMinIdle(10);
dataSource.setMaxActive(20);
dataSource.setMaxWait(60000);
return dataSource;
}
// 配置数据模板
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}

修改启动类:

java
1
2
3
4
5
6
7
8
9
10
11
public class DemoApplication {
public static void main(String[] args) {
// 加载配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
// 获取UserService对象
UserService userService = context.getBean(UserService.class);
// 调用业务方法
// 关闭容器
context.close();
}
}

4 核心功能

4.1 普通更新

对应增删改操作。

常用方法:

java
1
2
3
4
5
6
// 更新数据,返回影响的行数
public int update(final String sql) throws DataAccessException;
// 更新数据,支持传入参数,返回影响的行数
public int update(String sql, @Nullable Object... args) throws DataAccessException;
// 插入数据,获取自增主键
public int update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder) throws DataAccessException;

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* 新增用户
*/
public int insertUser(User user) {
String sql = "INSERT INTO user (name, age, email) VALUES (?, ?, ?)";
return jdbcTemplate.update(sql, user.getName(), user.getAge(), user.getEmail());
}
/**
* 新增用户,封装自增主键
*/
public int insertUserWithId(User user) {
String sql = "INSERT INTO user (name, age, email) VALUES (?, ?, ?)";
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
int insertRow = jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, user.getName());
ps.setInt(2, user.getAge());
ps.setString(3, user.getEmail());
return ps;
}, keyHolder);
user.setId(keyHolder.getKey().intValue());
return insertRow;
}
/**
* 更新用户
*/
public int updateUser(User user) {
String sql = "UPDATE user SET name = ?, age = ?, email = ? WHERE id = ?";
return jdbcTemplate.update(sql, user.getName(), user.getAge(), user.getEmail(), user.getId());
}
/**
* 删除用户
*/
public int deleteUserById(Integer id) {
String sql = "DELETE FROM user WHERE id = ?";
return jdbcTemplate.update(sql, id);
}

4.2 普通查询

4.2.1 查询简单数据

常用方法:

java
1
2
3
4
5
6
7
8
9
public <T> T query(PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse) throws DataAccessException;
// 查询单行简单数据
public <T> T queryForObject(String sql, Class<T> requiredType) throws DataAccessException;
// 查询单行简单数据,支持传入参数
public <T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException;
// 查询多行简单数据
public <T> List<T> queryForList(String sql, Class<T> elementType) throws DataAccessException;
// 查询多行简单数据,支持传入参数
public <T> List<T> queryForList(String sql, Class<T> elementType, @Nullable Object... args) throws DataAccessException;

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 查询用户总数
*/
public int selectUserCount() {
String sql = "SELECT COUNT(*) FROM user";
return jdbcTemplate.queryForObject(sql, Integer.class);
}
/**
* 查询所有用户ID
*/
public List<Integer> selectAllUserIdList() {
String sql = "SELECT id FROM user ORDER BY id";
return jdbcTemplate.queryForList(sql, Integer.class);
}

4.2.2 查询对象数据

常用方法:

java
1
2
3
4
5
6
7
8
// 查询单行对象数据
public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException;
// 查询单行对象数据,支持传入参数
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;
// 查询多行对象数据
public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException;
// 查询多行对象数据,支持传入参数
public <T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;

使用RowMapper接口可以将结果集的每一行映射为一个Java对象。

开发者可以自定义RowMapper接口,也可以使用内置的BeanPropertyRowMapper接口通过属性名称自动映射。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 根据ID查询用户
*/
public User selectUserById(Integer id) {
String sql = "SELECT * FROM user WHERE id = ?";
return jdbcTemplate.queryForObject(sql, (resultSet, i) -> {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
user.setAge(resultSet.getInt("age"));
user.setEmail(resultSet.getString("email"));
return user;
}, id);
}
/**
* 查询所有用户
*/
public List<User> selectAllUserList() {
String sql = "SELECT * FROM user ORDER BY id";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
}

4.2.3 查询Map数据

常用方法:

java
1
2
3
4
5
6
7
8
// 查询单行Map数据
public Map<String, Object> queryForMap(String sql) throws DataAccessException;
// 查询单行Map数据,支持传入参数
public Map<String, Object> queryForMap(String sql, @Nullable Object... args) throws DataAccessException;
// 查询多行Map数据
public List<Map<String, Object>> queryForList(String sql) throws DataAccessException;
// 查询多行Map数据,支持传入参数
public List<Map<String, Object>> queryForList(String sql, @Nullable Object... args) throws DataAccessException;

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 根据ID查询用户信息
*/
public Map<String, Object> selectUserInfoById(Integer id) {
String sql = "SELECT id, name FROM user WHERE id = ?";
return jdbcTemplate.queryForMap(sql, id);
}
/**
* 查询所有用户信息
*/
public List<Map<String, Object>> selectUserInfoList() {
String sql = "SELECT id, name FROM user ORDER BY id";
return jdbcTemplate.queryForList(sql);
}

4.3 批量更新

对应批量增删改操作。

常用方法:

java
1
2
// 批量更新数据,返回影响的行数数组
public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss) throws DataAccessException;

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* 批量插入用户
*/
public int[] batchInsert(List<User> users) {
String sql = "INSERT INTO user (name, age, email) 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.getName());
ps.setInt(2, user.getAge());
ps.setString(3, user.getEmail());
}
@Override
public int getBatchSize() {
return users.size();
}
});
}
/**
* 批量更新用户
*/
public int[] batchUpdate(List<User> users) {
String sql = "UPDATE user SET name = ?, age = ?, email = ? WHERE id = ?";
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.getName());
ps.setInt(2, user.getAge());
ps.setString(3, user.getEmail());
ps.setInt(4, user.getId());
}
@Override
public int getBatchSize() {
return users.size();
}
});
}
/**
* 批量删除用户
*/
public int[] batchDelete(List<Integer> ids) {
String sql = "DELETE FROM user WHERE id = ?";
return jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setInt(1, ids.get(i));
}
@Override
public int getBatchSize() {
return ids.size();
}
});
}

4.2 复杂查询

可以使用NamedParameterJdbcTemplate实现复杂查询,需要配置NamedParameterJdbcTemplate组件,支持XML配置和注解配置。

复杂查询:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 命名查询
*/
public List<User> selectUserListByConditions(String name, Integer minAge, Integer maxAge) {
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1 ");
MapSqlParameterSource params = new MapSqlParameterSource();
if (name != null && !name.isEmpty()) {
sql.append("AND name LIKE :name ");
params.addValue("name", "%" + name + "%");
}
if (minAge != null) {
sql.append("AND age >= :minAge ");
params.addValue("minAge", minAge);
}
if (maxAge != null) {
sql.append("AND age <= :maxAge ");
params.addValue("maxAge", maxAge);
}
sql.append("ORDER BY id");
return namedParameterJdbcTemplate.query(sql.toString(), params, new UserRowMapper());
}
/**
* 分页查询
*/
public List<User> selectUserListWithPagination(int offset, int limit) {
String sql = "SELECT * FROM user ORDER BY id LIMIT :limit OFFSET :offset";
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("limit", limit);
params.addValue("offset", offset);
return namedParameterJdbcTemplate.query(sql, params, new UserRowMapper());
}

5 底层原理

JdbcTemplate是Spring框架中用于简化JDBC操作的核心类,其底层原理基于模板方法模式和回调机制,实现了对JDBC复杂操作的封装。

基本流程:

  1. 获取连接:从配置的DataSource中获取数据库连接。
  2. 创建语句:根据SQL语句创建PreparedStatement对象。
  3. 设置参数:通过PreparedStatementSetter设置SQL参数。
  4. 执行操作:执行SQL语句。
  5. 处理结果:通过RowMapper或ResultSetExtractor处理ResultSet结果集。
  6. 资源清理:自动关闭资源。

主要接口:

  • PreparedStatementCreator:用于创建PreparedStatement对象。
  • PreparedStatementSetter:用于设置PreparedStatement的参数。
  • ResultSetExtractor:用于提取ResultSet的结果。
  • RowMapper:用于将ResultSet的每一行映射为Java对象。
  • StatementCallback:用于执行任意SQL语句。

评论