数据库场景的自动配置分析和整合测试
# 480.数据库场景的自动配置分析和整合测试
接下来讲数据访问,包含 SQL 和 NoSQL 的整合
# 数据源的自动配置
根据我们之前学习 web 开发,我们使用数据库,也只需引入相关的 starter,然后 SpringBoot 会自动帮我们引入很多组件,我们只需添加一些配置即可(例如数据库连接信息)
我们可以在文档中,看到很多有 starter 中包含 data 的,这就是和数据源相关的场景:
例如我们使用 JDBC,引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
2
3
4
通过分析依赖可知,其帮我们导入了数据源连接池技术(HikariCP),还有 JDBC(tx 是用于事务的):
注意,没有帮我们引入数据库驱动。为什么官方没有导入驱动呢?因为官方不知道我们使用的是什么数据源,MySQL?Oracle?所以我们得自己导入
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2
3
4
5
此外,SpringBoot 自动帮我们指定了版本号,因此我们不用写版本号也可。
我们可以点进
spring-boot-starter-parent
的依赖,然后再点进spring-boot-dependencies
,可以看到定义了很多的版本号:<mysql.version>8.0.21</mysql.version>
1
如果数据库驱动版本,和数据库版本不一致,也可以修改:
- 直接在依赖中使用
<version>
标签(Maven 的就近依赖原则) - 在 pom.xml 中使用
<properties>
标签,然后定义 MySQL 的版本号
接下来我们看看自动配置了什么,打开 spring-boot-autoconfigure-2.3.4.RELEASE.jar 包,其中和数据源相关的配置都在该目录:org\springframework\boot\autoconfigure\jdbc,有很多自动配置类:
DataSourceAutoConfiguration
:数据源的自动配置DataSourceTransactionManagerAutoConfiguration
:事务管理器的自动配置JdbcTemplateAutoConfiguration
:JDBC Template 的自动配置JndiDataSourceAutoConfiguration
:JNDI 自动配置XADataSourceAutoConfiguration
:分布式事务的控制- ......
我们来看看数据源的自动配置,其类上的注解如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
2
3
4
5
6
其中,
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
,这是响应式编程的数据源访问类,这里我们没有用到;@EnableConfigurationProperties(DataSourceProperties.class)
,这是绑定配置文件的,在DataSourceProperties
中,指定的前缀是spring.datasource
数据库连接池的配置:
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
2
3
4
5
6
7
8
9
根据 @ConditionalOnMissingBean
可知,如果我们自己没有配置数据源相关的配置,才会帮我们做数据库连接池的配置(也就是默认配置),默认连接池是 HikariDataSource
# 初始化数据库
接下来我们连接数据库。为了方便,使用之前学习 JDBC (opens new window) 时,用的数据库:
-- 创建数据库learjdbc:
DROP DATABASE IF EXISTS learnjdbc;
CREATE DATABASE learnjdbc;
-- 创建登录用户learn/口令learnpassword
CREATE USER IF NOT EXISTS learn@'%' IDENTIFIED BY 'learnpassword';
GRANT ALL PRIVILEGES ON learnjdbc.* TO learn@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
-- 创建表students:
USE learnjdbc;
CREATE TABLE students (
id BIGINT AUTO_INCREMENT NOT NULL,
name VARCHAR(50) NOT NULL,
gender TINYINT(1) NOT NULL,
grade INT NOT NULL,
score INT NOT NULL,
PRIMARY KEY(id)
) Engine=INNODB DEFAULT CHARSET=UTF8;
-- 插入初始数据:
INSERT INTO students (name, gender, grade, score) VALUES ('小明', 1, 1, 88);
INSERT INTO students (name, gender, grade, score) VALUES ('小红', 1, 1, 95);
INSERT INTO students (name, gender, grade, score) VALUES ('小军', 0, 1, 93);
INSERT INTO students (name, gender, grade, score) VALUES ('小白', 0, 1, 100);
INSERT INTO students (name, gender, grade, score) VALUES ('小牛', 1, 2, 96);
INSERT INTO students (name, gender, grade, score) VALUES ('小兵', 1, 2, 99);
INSERT INTO students (name, gender, grade, score) VALUES ('小强', 0, 2, 86);
INSERT INTO students (name, gender, grade, score) VALUES ('小乔', 0, 2, 79);
INSERT INTO students (name, gender, grade, score) VALUES ('小青', 1, 3, 85);
INSERT INTO students (name, gender, grade, score) VALUES ('小王', 1, 3, 90);
INSERT INTO students (name, gender, grade, score) VALUES ('小林', 0, 3, 91);
INSERT INTO students (name, gender, grade, score) VALUES ('小贝', 0, 3, 77);
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
# 配置连接信息
然后我们加上数据库连接的配置:
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
datasource:
url: jdbc:mysql://localhost:3306/learnjdbc?serverTimezone=UTC
username: learn
password: learnpassword
driver-class-name: com.mysql.cj.jdbc.Driver
2
3
4
5
6
7
8
9
10
注意,如果引入了数据源开发的场景,但是没有配置数据源连接信息(例如 url),那么启动是会报错的
# JdbcTemplateAutoConfiguration
自动配置类中,有个注解 import 了 JdbcTemplateConfiguration
:
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {
}
2
3
4
5
JdbcProperties
,就会读取 spring.jdbc
开头的配置并绑定:
@ConfigurationProperties(prefix = "spring.jdbc")
public class JdbcProperties {
2
JdbcTemplateConfiguration
,就帮我们初始化了 jdbcTemplate
对象,就是读取配置然后设置值:
@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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
为此,我们可以自定义 jdbcTemplate
的相关配置:
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
datasource:
url: jdbc:mysql://localhost:3306/learnjdbc
username: learn
password: learnpassword
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc:
template:
query-timeout: 3 # seconds
2
3
4
5
6
7
8
9
10
11
12
13
# CRUD
现在我们来测试下。我们在创建项目的过程中,SpringBoot 已经默认帮我们在 test 目录下,创建了一个测试类:
package com.peterjxl.learnspringbootwebadmin;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class LearnSpringBootWebAdminApplicationTests {
@Test
void contextLoads() {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
我们直接在该类增加代码即可。
由于我们引入了数据开发场景的依赖,因此会自动帮我们注入 JdbcTemplate,可以直接拿来用:
package com.peterjxl.learnspringbootwebadmin;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
@Slf4j
@SpringBootTest
class LearnSpringBootWebAdminApplicationTests {
@Autowired
JdbcTemplate jdbcTemplate;
@Test
void contextLoads() {
Long aLong = jdbcTemplate.queryForObject("select count(*) from students", Long.class);
log.info("记录总数:{}", aLong);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
查询完后也不用释放连接之类的,SpringBoot 会自动帮我们做。然后运行,可以看到能正常查询出结果。
# 源码
已将本文源码上传到 Gitee (opens new window) 或 GitHub (opens new window) 的分支 demo9,读者可以通过切换分支来查看本文的示例代码