Spring 之数据源
本文基于 Spring Boot 2.7.3 版本。
Spring Boot 数据源基本配置 Spring Boot 提供了一系列 spring.datasource.*
配置来控制 DataSource
的配置。用户可以在 application.properties
或 application.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
需要根据实际情况,替换 url
、username
、password
。
Spring Boot 连接嵌入式数据源 使用内存嵌入式数据库开发应用程序通常很方便。显然,内存数据库不提供持久存储。使用者需要在应用程序启动时填充数据库,并准备在应用程序结束时丢弃数据。
Spring Boot 可以自动配置嵌入式数据库 H2 、HSQL 和 Derby 。使用者无需提供任何连接 URL,只需要包含对要使用的嵌入式数据库的构建依赖项。如果类路径上有多个嵌入式数据库,需要设置 spring.datasource.embedded-database-connection
配置属性来控制使用哪一个。将该属性设置为 none 会禁用嵌入式数据库的自动配置。
注意:如果在测试中使用此功能,无论使用多少应用程序上下文,整个测试套件都会重用同一个数据库。如果要确保每个上下文都有一个单独的嵌入式数据库,则应将 spring.datasource.generate-unique-name
设置为 true。
下面,通过一个实例展示如何连接 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-data-jdbc
在生产环境中,出于性能考虑,一般会通过数据库连接池连接数据源。
除了 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 spring.datasource.hikari.maximum-pool-size = 10 spring.datasource.hikari.minimum-idle = 10 spring.datasource.hikari.connection-timeout = 60000 spring.datasource.hikari.idle-timeout = 600000 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 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jdbc</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </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:
Spring Boot 连接多数据源
完整示例:spring-boot-data-jdbc-multi-datasource
Spring Boot 连接多数据源所需要的依赖并无不同,主要差异在于数据源的配置。Spring Boot 默认的数据源配置类为 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
。使用者只要指定一些必要的 spring.datasource 配置,DataSourceAutoConfiguration
类就会自动完成剩下的数据源实例化工作。
多数据源配置 下面的示例中,自定义了一个数据源配置类,通过读取不同的 spring.datasource.xxx 来完成对于不同数据源的实例化工作。对于 JDBC 来说,最重要的,就是实例化 DataSource
和 JdbcTemplate
。
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.properties
或 application.yml
配置文件中也必须以 @ConfigurationProperties
所指定的配置前缀进行配置:
1 2 3 4 5 6 7 8 9 10 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 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 ; } mysqlUserDao.recreateTable(); 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: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:test21 :16 :44.726 [main] [INFO ] i .g .d .s .d .SpringBootDataJdbcMultiDataSourceApplication .run - Connect to h2 datasource success.
Spring 之数据源 如果你的项目是传统的 Spring 项目,当然也可以轻松建立数据源连接,只是需要自行设置的配置更多一些。
引入 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 >
Spring 配置数据源 Spring 配置数据源有多种方式,下面一一列举:
使用 JNDI 数据源 如果 Spring 应用部署在支持 JNDI 的 WEB 服务器上(如 WebSphere、JBoss、Tomcat 等),就可以使用 JNDI 获取数据源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee ="http://www.springframework.org/schema/jee" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd" > <bean id ="dataSource" class ="org.springframework.jndi.JndiObjectFactoryBean" > <property name ="jndiName" value ="java:comp/env/jdbc/orclight" /> </bean > <jee:jndi-lookup id ="dataSource" jndi-name =" java:comp/env/jdbc/orclight" /> </beans >
使用数据库连接池 Spring 本身并没有提供数据库连接池的实现,需要自行选择合适的数据库连接池。下面是一个使用 Druid 作为数据库连接池的示例:
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 <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" init-method ="init" destroy-method ="close" > <property name ="driverClassName" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> <property name ="initialSize" value ="1" /> <property name ="minIdle" value ="1" /> <property name ="maxActive" value ="10" /> <property name ="maxWait" value ="10000" /> <property name ="timeBetweenEvictionRunsMillis" value ="60000" /> <property name ="minEvictableIdleTimeMillis" value ="300000" /> <property name ="testWhileIdle" value ="true" /> <property name ="testOnBorrow" value ="true" /> <property name ="testOnReturn" value ="false" /> <property name ="poolPreparedStatements" value ="true" /> <property name ="maxPoolPreparedStatementPerConnectionSize" value ="20" /> <property name ="defaultAutoCommit" value ="true" /> <property name ="validationQuery" value ="select 1 " /> <property name ="filters" value ="stat" /> </bean >
基于 JDBC 驱动的数据源 1 2 3 4 5 6 <bean id ="dataSource" 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 >
SpringBoot 数据源配置
Spring Boot 数据库配置官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/data.html#data.sql
通过前面的实战,我们已经知道了 Spring、Spring Boot 是如何连接数据源,并通过 JDBC 方式访问数据库。
SpringBoot 数据源的配置方式是在 application.properties
或 application.yml
文件中指定 spring.datasource.*
的配置。
(1)数据源基本配置方式是指定 url、用户名、密码
1 2 3 spring.datasource.url =jdbc:mysql://localhost/test spring.datasource.username =dbuser spring.datasource.password =dbpass
(2)配置 JNDI
如果想要通过 JNDI 方式连接数据源,可以采用如下方式:
1 spring.datasource.jndi-name =java:jboss/datasources/customers
DataSourceAutoConfiguration 类 显而易见,Spring Boot 的配置更加简化,那么, Spring Boot 做了哪些工作,使得接入更加便捷呢?奥秘就在于 spring-boot-autoconfigure
jar 包,其中定义了大量的 Spring Boot 自动配置类。其中,与数据库访问相关的比较核心的配置类有:
DataSourceAutoConfiguration
:数据源自动配置类
JdbcTemplateAutoConfiguration
:JdbcTemplate
自动配置类
DataSourceTransactionManagerAutoConfiguration
:数据源事务管理自动配置类
JndiDataSourceAutoConfiguration
:JNDI 数据源自动配置类
EmbeddedDataSourceConfiguration
:嵌入式数据库数据源自动配置类
等等
这些自动配置类会根据各种条件控制核心类的实例化。
DataSourceAutoConfiguration
是数据源自动配置类,它负责实例化 DataSource
。
DataSourceAutoConfiguration
的源码如下(省略部分代码):
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 @AutoConfiguration(before = SqlInitializationAutoConfiguration.class) @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory") @EnableConfigurationProperties(DataSourceProperties.class) @Import(DataSourcePoolMetadataProvidersConfiguration.class) public class DataSourceAutoConfiguration { @Configuration(proxyBeanMethods = false) @Conditional(EmbeddedDatabaseCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import(EmbeddedDataSourceConfiguration.class) protected static class EmbeddedDatabaseConfiguration { } @Configuration(proxyBeanMethods = false) @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class }) protected static class PooledDataSourceConfiguration { } static class PooledDataSourceCondition extends AnyNestedCondition { } static class PooledDataSourceAvailableCondition extends SpringBootCondition { } static class EmbeddedDatabaseCondition extends SpringBootCondition { } }
DataSourceAutoConfiguration
类的源码解读:
DataSourceProperties
是 DataSourceAutoConfiguration
的配置选项类,允许使用者通过设置选项控制 DataSource
初始化行为。
DataSourceAutoConfiguration
通过 @Import
注解引入 DataSourcePoolMetadataProvidersConfiguration
类。
DataSourceAutoConfiguration
中定义了两个内部类:嵌入式数据源配置类 EmbeddedDatabaseConfiguration
和 池化数据源配置类 PooledDataSourceConfiguration
,分别标记了不同的实例化条件。
当满足 EmbeddedDatabaseConfiguration
的示例化条件时,将引入 EmbeddedDataSourceConfiguration
类初始化数据源,这个类实际上是加载嵌入式数据源驱动的 ClassLoader 去进行初始化。
当满足 PooledDataSourceConfiguration
的示例化条件时,将引入 DataSourceConfiguration.Hikari.class
、DataSourceConfiguration.Tomcat.class
、DataSourceConfiguration.Dbcp2.class
、DataSourceConfiguration.OracleUcp.class
、DataSourceConfiguration.Generic.class
、DataSourceJmxConfiguration.class
这些配置类,分别对应不同的数据库连接池方式。具体选用哪种数据库连接池,可以通过 spring.datasource.type
配置指定。其中,Hikari 是 Spring Boot 默认的数据库连接池,spring-boot-starter-data-jdbc 中内置了 Hikari 连接池驱动包。如果想要替换其他数据库连接池,前提是必须先手动引入对应的连接池驱动包。
参考资料