Spring 之数据源

本文基于 Spring Boot 2.7.3 版本。

Spring Boot 数据源基本配置

Spring Boot 提供了一系列 spring.datasource.* 配置来控制 DataSource 的配置。用户可以在 application.propertiesapplication.yml 文件中指定数据源配置。这些配置项维护在 DataSourceProperties

下面是一个最基本的 mysql 数据源配置示例(都是必填项):

1
2
3
4
5
6
7
8
# 数据库访问地址
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

Spring Boot 连接池化数据源

在生产环境中,出于性能考虑,一般会通过数据库连接池连接数据源。

除了 DataSourceProperties 中的数据源通用配置以外,Spring Boot 还支持通过使用类似spring.datasource.hikari.*spring.datasource.tomcat.*spring.datasource.dbcp2.*spring.datasource.oracleucp.* 的前缀来配置指定的数据库连接池属性。

下面,就是一份 hikari 的连接池配置示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 连接池名称
spring.datasource.hikari.pool-name = SpringTutorialHikariPool
# 最大连接数,小于等于 0 会被重置为默认值 10;大于零小于 1 会被重置为 minimum-idle 的值
spring.datasource.hikari.maximum-pool-size = 10
# 最小空闲连接,默认值10,小于 0 或大于 maximum-pool-size,都会重置为 maximum-pool-size
spring.datasource.hikari.minimum-idle = 10
# 连接超时时间(单位:毫秒),小于 250 毫秒,会被重置为默认值 30 秒
spring.datasource.hikari.connection-timeout = 60000
# 空闲连接超时时间,默认值 600000(10分钟),大于等于 max-lifetime 且 max-lifetime>0,会被重置为0;不等于 0 且小于 10 秒,会被重置为 10 秒
# 只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放
spring.datasource.hikari.idle-timeout = 600000
# 连接最大存活时间,不等于 0 且小于 30 秒,会被重置为默认值 30 分钟。该值应该比数据库所设置的超时时间短
spring.datasource.hikari.max-lifetime = 540000

Spring Boot 会按以下顺序检测连接池是否可用,如果可用就选择对应的池化 DataSource

HikariCP -> Tomcat pooling DataSource -> DBCP2 -> Oracle UCP

用户也可以通过 spring.datasource.type 来指定数据源类型。

此外,也可以使用 DataSourceBuilder 手动配置其他连接池。如果自定义 DataSource bean,则不会发生自动配置。 DataSourceBuilder 支持以下连接池:

  • HikariCP
  • Tomcat pooling Datasource
  • Commons DBCP2
  • Oracle UCP & OracleDataSource
  • Spring Framework’s SimpleDriverDataSource
  • H2 JdbcDataSource
  • PostgreSQL PGSimpleDataSource
  • C3P0

引入 Spring Boot 依赖

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

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>

测试单数据源连接

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 Boot 连接嵌入式数据源

使用内存嵌入式数据库开发应用程序通常很方便。显然,内存数据库不提供持久存储。使用者需要在应用程序启动时填充数据库,并准备在应用程序结束时丢弃数据。

Spring Boot 可以自动配置嵌入式数据库 H2HSQLDerby。使用者无需提供任何连接 URL,只需要包含对要使用的嵌入式数据库的构建依赖项。

下面,通过一个实例展示如何连接 H2 嵌入式数据库。

(1)在 pom.xml 中引入所需要的依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

(2)数据源配置

1
2
3
4
spring.datasource.jdbc-url = jdbc:h2:mem:test
spring.datasource.driver-class-name = org.h2.Driver
spring.datasource.username = sa
spring.datasource.password =

Spring Boot 连接多数据源

Spring Boot 连接多数据源所需要的依赖并无不同,主要差异在于数据源的配置。Spring Boot 默认的数据源配置类为 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration。使用者只要指定一些必要的 spring.datasource 配置,DataSourceAutoConfiguration 类就会自动完成剩下的数据源实例化工作。

多数据源配置

下面的示例中,自定义了一个数据源配置类,通过读取不同的 spring.datasource.xxx 来完成对于不同数据源的实例化工作。对于 JDBC 来说,最重要的,就是实例化 DataSourceJdbcTemplate

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
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class DataSourceConfig {

@Primary
@Bean("mysqlDataSource")
@ConfigurationProperties(prefix = "spring.datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}

@Primary
@Bean("mysqlJdbcTemplate")
public JdbcTemplate mysqlJdbcTemplate(@Qualifier("mysqlDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}

@Bean("h2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.h2")
public DataSource h2DataSource() {
return DataSourceBuilder.create().build();
}

@Bean(name = "h2JdbcTemplate")
public JdbcTemplate h2JdbcTemplate(@Qualifier("h2DataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}

}

application.propertiesapplication.yml 配置文件中也必须以 @ConfigurationProperties 所指定的配置前缀进行配置:

1
2
3
4
5
6
7
8
9
10
# 数据源一:Mysql
spring.datasource.mysql.jdbc-url = jdbc:mysql://localhost:3306/spring_tutorial?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.mysql.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.mysql.username = root
spring.datasource.mysql.password = root
# 数据源一:H2
spring.datasource.h2.jdbc-url = jdbc:h2:mem:test
spring.datasource.h2.driver-class-name = org.h2.Driver
spring.datasource.h2.username = sa
spring.datasource.h2.password =

测试多数据源连接

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
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 java.sql.SQLException;
import javax.sql.DataSource;

@SpringBootApplication
public class SpringBootDataJdbcMultiDataSourceApplication implements CommandLineRunner {

private static final Logger log = LoggerFactory.getLogger(SpringBootDataJdbcMultiDataSourceApplication.class);

private final UserDao mysqlUserDao;

private final UserDao h2UserDao;

public SpringBootDataJdbcMultiDataSourceApplication(@Qualifier("mysqlUserDao") UserDao mysqlUserDao,
@Qualifier("h2UserDao") UserDao h2UserDao) {
this.mysqlUserDao = mysqlUserDao;
this.h2UserDao = h2UserDao;
}

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

@Override
public void run(String... args) throws Exception {

if (mysqlUserDao != null && mysqlUserDao.getJdbcTemplate() != null) {
printDataSourceInfo(mysqlUserDao.getJdbcTemplate());
log.info("Connect to mysql datasource success.");
} else {
log.error("Connect to mysql datasource failed!");
return;
}

if (h2UserDao != null) {
printDataSourceInfo(h2UserDao.getJdbcTemplate());
log.info("Connect to h2 datasource success.");
} else {
log.error("Connect to h2 datasource failed!");
return;
}

// 主数据源执行 JDBC SQL
mysqlUserDao.recreateTable();

// 次数据源执行 JDBC SQL
h2UserDao.recreateTable();
}

private void printDataSourceInfo(JdbcTemplate jdbcTemplate) throws SQLException {

DataSource dataSource = jdbcTemplate.getDataSource();

Connection connection;
if (dataSource != null) {
connection = dataSource.getConnection();
} else {
log.error("Get dataSource failed!");
return;
}

if (connection != null) {
log.info("DataSource Url: {}", connection.getMetaData().getURL());
} else {
log.error("Connect to datasource failed!");
}
}

}

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

1
2
3
4
5
21:16:44.654 [main] [INFO ] i.g.d.s.d.SpringBootDataJdbcMultiDataSourceApplication.printDataSourceInfo - DataSource Url: jdbc:mysql://localhost:3306/spring_tutorial?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
21:16:44.654 [main] [INFO ] i.g.d.s.d.SpringBootDataJdbcMultiDataSourceApplication.run - Connect to mysql datasource success.

21:16:44.726 [main] [INFO ] i.g.d.s.d.SpringBootDataJdbcMultiDataSourceApplication.printDataSourceInfo - DataSource Url: jdbc:h2:mem:test
21:16:44.726 [main] [INFO ] i.g.d.s.d.SpringBootDataJdbcMultiDataSourceApplication.run - Connect to h2 datasource success.

参考资料