Spring 之 JDBC

Spring 之 JDBC

JDBC 是 Java 语言中用来规范客户端程序如何访问数据库的应用程序接口,提供了增、删、改、查数据库的方法。

JDBC 入门示例

JDBC 的工作步骤大致如下:

  1. 创建实体类。
  2. 声明数据库读写接口的 DAO 接口。定义 DAO 的好处在于对于数据层上层的业务,调用 DAO 时仅关注对外暴露的读写方法,而不考虑底层的具体持久化方式。这样,便于替换持久化方式。
  3. 创建一个 DAO 接口的实现类,使用 Spring 的 JDBC 模板去实现接口。
  4. 最后,定义一个 DAO 接口的实现类的 JavaBean,并将数据源注入进去。

假设,我们要通过 Spring + JDBC 访问一张 Mysql 数据表 useruser 表的数据结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 创建用户表
CREATE TABLE `user` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '用户名',
`age` INT(3) NOT NULL DEFAULT 0 COMMENT '年龄',
`address` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '地址',
`email` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '邮件',
PRIMARY KEY (`id`),
UNIQUE (`name`)
) COMMENT = '用户表';

INSERT INTO `user` (`name`, `age`, `address`, `email`)
VALUES ('张三', 18, '北京', 'xxx@163.com');
INSERT INTO `user` (`name`, `age`, `address`, `email`)
VALUES ('李四', 19, '上海', 'xxx@163.com');

定义实体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Objects;

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String address;
private String email;
}

定义 DAO 接口

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
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

/**
* user 表 Dao 接口
*
* @author <a href="mailto:forbreak@163.com">Zhang Peng</a>
* @since 2019-11-18
*/
public interface UserDao {

// DML
// -------------------------------------------------------------------
void insert(User user);

void batchInsert(List<User> users);

void deleteByName(String name);

void deleteAll();

void update(User user);

Integer count();

List<User> list();

User queryByName(String name);

JdbcTemplate getJdbcTemplate();

// DDL
// -------------------------------------------------------------------
void truncate();

void recreateTable();

}

定义 DAO 实现类

通过 JdbcTemplate 执行对应数据源符合语法的 SQL,即可完成各种数据库访问。

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package io.github.dunwu.springboot.core.data.jdbc;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
* user 表 Dao 接口实现类
*
* @author <a href="mailto:forbreak@163.com">Zhang Peng</a>
* @since 2019-11-18
*/
@Repository
public class UserDaoImpl implements UserDao {

private JdbcTemplate jdbcTemplate;

public UserDaoImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

@Override
public void insert(User user) {
jdbcTemplate.update("INSERT INTO user(name, age, address, email) VALUES(?, ?, ?, ?)",
user.getName(), user.getAge(), user.getAddress(), user.getEmail());
}

@Override
@Transactional(rollbackFor = Exception.class)
public void batchInsert(List<User> users) {
String sql = "INSERT INTO user(name, age, address, email) VALUES(?, ?, ?, ?)";

List<Object[]> params = new ArrayList<>();

users.forEach(user -> {
params.add(new Object[] { user.getName(), user.getAge(), user.getAddress(), user.getEmail() });
});
jdbcTemplate.batchUpdate(sql, params);
}

@Override
public void deleteByName(String name) {
jdbcTemplate.update("DELETE FROM user WHERE name = ?", name);
}

@Override
@Transactional(rollbackFor = Exception.class)
public void deleteAll() {
jdbcTemplate.execute("DELETE FROM user");
}

@Override
public void update(User user) {
jdbcTemplate.update("UPDATE user SET name=?, age=?, address=?, email=? WHERE id=?",
user.getName(), user.getAge(), user.getAddress(), user.getEmail(), user.getId());
}

@Override
public Integer count() {
try {
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM user", Integer.class);
} catch (EmptyResultDataAccessException e) {
return null;
}
}

@Override
public List<User> list() {
return jdbcTemplate.query("SELECT * FROM user", new BeanPropertyRowMapper<>(User.class));
}

@Override
public User queryByName(String name) {
try {
return jdbcTemplate.queryForObject("SELECT * FROM user WHERE name = ?",
new BeanPropertyRowMapper<>(User.class), name);
} catch (EmptyResultDataAccessException e) {
return null;
}
}

@Override
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}

@Override
public void truncate() {
jdbcTemplate.execute("TRUNCATE TABLE user");
}

@Override
public void recreateTable() {
jdbcTemplate.execute("DROP TABLE IF EXISTS user");

String sqlStatement =
"CREATE TABLE IF NOT EXISTS user (\n"
+ " id BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Id',\n"
+ " name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '用户名',\n"
+ " age INT(3) NOT NULL DEFAULT 0 COMMENT '年龄',\n"
+ " address VARCHAR(255) NOT NULL DEFAULT '' COMMENT '地址',\n"
+ " email VARCHAR(255) NOT NULL DEFAULT '' COMMENT '邮件',\n"
+ " PRIMARY KEY (id)\n"
+ ") COMMENT = '用户表';";
jdbcTemplate.execute(sqlStatement);
}

}

测试类

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;

import java.util.ArrayList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@Slf4j
@Rollback
@SpringBootTest(classes = { SpringBootDataJdbcApplication.class })
public class DataJdbcMysqlDataSourceTest {

@Autowired
private UserDao userDAO;

@BeforeEach
public void before() {
userDAO.truncate();
}

@Test
public void insert() {
userDAO.insert(new User("张三", 18, "北京", "user1@163.com"));
User linda = userDAO.queryByName("张三");
assertThat(linda).isNotNull();
}

@Test
public void batchInsert() {
List<User> users = new ArrayList<>();
users.add(new User("张三", 18, "北京", "user1@163.com"));
users.add(new User("李四", 19, "上海", "user1@163.com"));
users.add(new User("王五", 18, "南京", "user1@163.com"));
users.add(new User("赵六", 20, "武汉", "user1@163.com"));

userDAO.batchInsert(users);
int count = userDAO.count();
assertThat(count).isEqualTo(4);

List<User> list = userDAO.list();
assertThat(list).isNotEmpty().hasSize(4);
list.forEach(user -> {
log.info(user.toString());
});
}

@Test
public void delete() {
List<User> users = new ArrayList<>();
users.add(new User("张三", 18, "北京", "user1@163.com"));
users.add(new User("李四", 19, "上海", "user1@163.com"));
users.add(new User("王五", 18, "南京", "user1@163.com"));
users.add(new User("赵六", 20, "武汉", "user1@163.com"));
userDAO.batchInsert(users);

userDAO.deleteByName("张三");
User user = userDAO.queryByName("张三");
assertThat(user).isNull();

userDAO.deleteAll();
List<User> list = userDAO.list();
assertThat(list).isEmpty();
}

@Test
public void update() {
userDAO.insert(new User("张三", 18, "北京", "user1@163.com"));
User oldUser = userDAO.queryByName("张三");
oldUser.setName("张三丰");
userDAO.update(oldUser);
User newUser = userDAO.queryByName("张三丰");
assertThat(newUser).isNotNull();
}

}

Spring Boot JDBC

完整示例:spring-boot-data-jdbc

引入 Spring Boot 依赖

你可以通过 Spring Boot 官方的初始化器(Spring Initializr)选择需要的组件来创建一个 Spring Boot 工程。或者,直接在 pom.xml 中引入所需要的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>

配置数据源

引入依赖后,需要在 application.propertiesapplication.yml 文件中指定数据源配置。

下面是一个最基本的数据源配置示例:

1
2
3
4
spring.datasource.url = jdbc:mysql://localhost:3306/spring_tutorial?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.username = root
spring.datasource.password = root

需要根据实际情况,替换 urlusernamepassword

测试

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
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.Connection;
import javax.sql.DataSource;

@Slf4j
@SpringBootApplication
public class SpringBootDataJdbcApplication implements CommandLineRunner {

private final JdbcTemplate jdbcTemplate;

public SpringBootDataJdbcApplication(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public static void main(String[] args) {
SpringApplication.run(SpringBootDataJdbcApplication.class, args);
}

@Override
public void run(String... args) throws Exception {
DataSource dataSource = jdbcTemplate.getDataSource();

Connection connection;
if (dataSource != null) {
connection = dataSource.getConnection();
} else {
log.error("连接数据源失败!");
return;
}

if (connection != null) {
log.info("数据源 Url: {}", connection.getMetaData().getURL());
} else {
log.error("连接数据源失败!");
}
}

}

运行 main 方法后,控制台会输出以下内容,表示数据源连接成功:

1
20:50:18.449 [main] [INFO ] i.g.d.s.d.SpringBootDataJdbcApplication.run - 数据源 Url: jdbc:mysql://localhost:3306/spring_tutorial?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8

Spring JDBC

完整示例:spring-data-jdbc

spring-boot-starter-data-jdbc 引入了 spring-jdbc ,其 JDBC 特性就是基于 spring-jdbc

引入 Spring 依赖

在 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
    <dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
</dependencies>
</project>

基于 JDBC 驱动的数据源配置

下面是一个 mysql 的 JDBC 数据源配置实例:

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">

<!-- 引入配置文件 -->
<context:property-placeholder location="classpath:properties/mysql.properties" />

<!-- 使用JDBC驱动的数据源 -->
<!-- (1)在每个连接请求时都会返回一个新建的连接。性能不高 -->
<bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>

<!-- (2)在每个连接请求时都会返回同一个连接。不适用于多线程 -->
<bean id="dataSource2" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>

<!-- JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource1" />
</bean>
<bean id="userDao" class="io.github.dunwu.springboot.data.jdbc.UserDaoImpl">
<constructor-arg ref="jdbcTemplate" />
</bean>

<!-- 初始化数据表结构 -->
<jdbc:initialize-database data-source="dataSource1" ignore-failures="ALL">
<jdbc:script location="classpath:sql/schema.sql" />
<jdbc:script location="classpath:sql/data.sql" />
</jdbc:initialize-database>
</beans>

测试

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

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.io.IOException;
import java.sql.SQLException;

@SuppressWarnings("all")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:data/spring-mysql.xml" })
public class MysqlJdbcTest {

@Autowired
private ApplicationContext ctx;

@Before
public void before() {
ctx = JdbcDemo.getMysqlApplicationContext();
}

@Test
public void testExecJdbcOper() throws SQLException, IOException {
UserDao userDao = (UserDaoImpl) ctx.getBean("userDao");
JdbcDemo.execJdbcOper(userDao);
}

@After
public void after() {
((ClassPathXmlApplicationContext) ctx).close();
}

}

JdbcTemplate API

Spring 将数据访问的样板式代码提取到模板类中。Spring 提供了 3 个 JDBC 模板类:

  • JdbcTemplate:最基本的 Spring JDBC 模板,这个模板支持最简单的 JDBC 数据库访问功能以及简单的索引参数查询。
  • SimpleJdbcTemplate:改模板类利用 Java 5 的一些特性,如自动装箱、泛型以及可变参数列表来简化 JDBC 模板的使用。
  • NamedParameterJdbcTemplate:使用该模板类执行查询时,可以将查询值以命名参数的形式绑定到 SQL 中,而不是使用简单的索引参数。

spring-jdbc 最核心的 API 无疑就是 JdbcTemplate,可以说所有的 JDBC 数据访问,几乎都是围绕着这个类去工作的。Spring 对数据库的操作在 Jdbc 层面做了深层次的封装,利用依赖注入,把数据源配置装配到 JdbcTemplate 中,再由 JdbcTemplate 负责具体的数据访问。

JdbcTemplate 主要提供以下几类方法:

  • execute 方法:可以用于执行任何 SQL 语句,一般用于执行 DDL 语句;
  • update 方法及 batchUpdate 方法:update 方法用于执行新增、修改、删除等语句;batchUpdate 方法用于执行批处理相关语句;
  • query 方法及 queryForXXX 方法:用于执行查询相关语句;
  • call 方法:用于执行存储过程、函数相关语句。

为了方便演示,以下增删改查操作都围绕一个名为 user 的表(该表的主键 id 是自增序列)进行,该表的数据实体如下:

1
2
3
4
5
6
7
public class User {
private Integer id;
private String name;
private Integer age;

// 省略 getter/setter
}

数据实体只要是一个纯粹的 Java Bean 即可,无需任何注解修饰。

execute 方法

使用 execute 执行 DDL 语句,创建一个名为 test 的数据库,并在此数据库下新建一个名为 user 的表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void recreateTable() {
jdbcTemplate.execute("DROP DATABASE IF EXISTS test");
jdbcTemplate.execute("CREATE DATABASE test");
jdbcTemplate.execute("USE test");
jdbcTemplate.execute("DROP TABLE if EXISTS user");
jdbcTemplate.execute("DROP TABLE if EXISTS user");
// @formatter:off
StringBuilder sb = new StringBuilder();
sb.append("CREATE TABLE user (id int (10) unsigned NOT NULL AUTO_INCREMENT,\n")
.append("name varchar (64) NOT NULL DEFAULT '',\n")
.append("age tinyint (3) NOT NULL DEFAULT 0,\n")
.append("PRIMARY KEY (ID));\n");
// @formatter:on
jdbcTemplate.execute(sb.toString());
}

update 方法

新增数据

1
2
3
public void insert(String name, Integer age) {
jdbcTemplate.update("INSERT INTO user(name, age) VALUES(?, ?)", name, age);
}

删除数据

1
2
3
public void delete(String name) {
jdbcTemplate.update("DELETE FROM user WHERE name = ?", name);
}

修改数据

1
2
3
public void update(User user) {
jdbcTemplate.update("UPDATE USER SET name=?, age=? WHERE id=?", user.getName(), user.getAge(), user.getId());
}

批处理

1
2
3
4
5
6
7
8
9
10
public void batchInsert(List<User> users) {
String sql = "INSERT INTO user(name, age) VALUES(?, ?)";

List<Object[]> params = new ArrayList<>();

users.forEach(item -> {
params.add(new Object[] {item.getName(), item.getAge()});
});
jdbcTemplate.batchUpdate(sql, params);
}

query 方法

查单个对象

1
2
3
4
5
6
7
8
public User queryByName(String name) {
try {
return jdbcTemplate
.queryForObject("SELECT * FROM user WHERE name = ?", new BeanPropertyRowMapper<>(User.class), name);
} catch (EmptyResultDataAccessException e) {
return null;
}
}

查多个对象

1
2
3
public List<User> list() {
return jdbcTemplate.query("select * from USER", new BeanPropertyRowMapper(User.class));
}

获取某个记录某列或者 count、avg、sum 等函数返回唯一值

1
2
3
4
5
6
7
public Integer count() {
try {
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM user", Integer.class);
} catch (EmptyResultDataAccessException e) {
return null;
}
}

SpringBoot JDBC 配置

JdbcTemplateAutoConfiguration 类

JdbcTemplateAutoConfigurationJdbcTemplate 自动配置类,它负责实例化 JdbcTemplate

1
2
3
4
5
6
7
8
9
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {

}

JdbcTemplateAutoConfiguration 类的源码解读:

  • @AutoConfigureAfter(DataSourceAutoConfiguration.class) 表明 JdbcTemplateAutoConfiguration 必须在 DataSourceAutoConfiguration 执行完之后才开始工作,这意味着:JdbcTemplate 的初始化必须在 DataSource 初始化之后。
  • JdbcPropertiesJdbcTemplateAutoConfiguration 的配置选项类,允许使用者通过设置选项控制 JdbcTemplate 初始化行为。
  • @Import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class }) 表明引入 JdbcTemplateConfigurationNamedParameterJdbcTemplateConfiguration 两个配置类,具体的实例化 JdbcTemplate 的工作也是放在这两个配置中完成。

JdbcTemplateConfiguration 类

JdbcTemplateConfiguration 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(JdbcOperations.class)
class JdbcTemplateConfiguration {

@Bean
@Primary
JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
JdbcProperties.Template template = properties.getTemplate();
jdbcTemplate.setFetchSize(template.getFetchSize());
jdbcTemplate.setMaxRows(template.getMaxRows());
if (template.getQueryTimeout() != null) {
jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());
}
return jdbcTemplate;
}

}

JdbcTemplateConfiguration 源码解读:JdbcTemplateConfiguration 中根据 DataSourceJdbcProperties 实例化了一个 JdbcTemplate

NamedParameterJdbcTemplateConfiguration 类

NamedParameterJdbcTemplateConfiguration 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(JdbcTemplate.class)
@ConditionalOnMissingBean(NamedParameterJdbcOperations.class)
class NamedParameterJdbcTemplateConfiguration {

@Bean
@Primary
NamedParameterJdbcTemplate namedParameterJdbcTemplate(JdbcTemplate jdbcTemplate) {
return new NamedParameterJdbcTemplate(jdbcTemplate);
}

}

NamedParameterJdbcTemplateConfiguration 源码解读:NamedParameterJdbcTemplateConfiguration 中根据 JdbcTemplate 实例化了一个 NamedParameterJdbcTemplate

spring-data-jdbc

Spring Data 项目包含了对 JDBC 的存储库支持,并将自动为 CrudRepository 上的方法生成 SQL。对于更高级的查询,提供了 @Query 注解。

当 classpath 上存在必要的依赖项时,Spring Boot 将自动配置 Spring Data 的 JDBC 存储库。它们可以通过 spring-boot-starter-data-jdbc 的单一依赖项添加到项目中。如有必要,可以通过将 @EnableJdbcRepositories 批注或 JdbcConfiguration 子类添加到应用程序来控制 Spring Data JDBC 的配置。

更多 Spring Data JDBC 细节,可以参考 Spring Data JDBC 官方文档

参考资料