参考资料
SpringBoot官方文档#Working with SQL Databases
Spring Boot默认HikariDataSource配置
具体配置参考Druid Spring Boot Starter
1 基本概念
1.1 JDBC
JDBC 的全称是Java数据库连接(Java Database Connectivity,简称JDBC),是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口。
提供了诸如查询和更新数据库中数据的方法,JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。
JDBC API 主要位于JDK中的java.sql
包中(之后扩展的内容位于 javax.sql 包中),主要包括(这几个代表接口,需驱动程序提供者来具体实现):
-
DriverManager
:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection)。 -
Driver
:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。 -
Connection
:数据库连接,负责与进行数据库间通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement。 -
Statement
:用以执行SQL查询和更新(针对静态SQL语句和单次执行)。 -
PreparedStatement
:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。 -
CallableStatement
:用以调用数据库中的存储过程。 -
SQLException
:代表在数据库连接的建立和关闭和SQL语句的执行过程中发生了例外情况(即错误)。
1.2 数据源
在java.sql
中并没有数据源(Data Source)的概念。这是由于在java.sql中包含的是JDBC内核API.
另外还有个javax.sql
包,其中包含了JDBC标准的扩展API。而关于数据源(Data Source)的定义,就在javax.sql这个扩展包中。
实际上,在JDBC内核API的实现下,就已经可以实现对数据库的访问了,那么我们为什么还需要数据源呢?主要出于以下几个目的:
-
封装关于数据库访问的各种参数,实现统一管理
-
通过对数据库的连接池管理,节省开销并提高效率
在Java的生态环境中,已经有非常多优秀的开源数据源,比如:DBCP、C3P0、Druid、HikariCP(重点)等。
在Spring Boot 2.x中,对数据源的选择,采用了目前性能最佳的HikariCP。这个就是 Spring Boot2.x 中的默认数据源配置。
2 整合步骤
对于数据访问层,无论是SQL还是NOSQL,Spring Boot默认采用整合
Spring Data
的方式进行统一处理,添加大量自动配置,屏蔽了很多设置。
引入各种xxxTemplate,xxxRepository来简化我们对数据访问层的操作。
对我们来说只需要进行简单的设置即可。
版本:
SpringBoot(v2.3.2.RELEASE)
注意:
引入的组件,如果在父POM全局管理spring-boot依赖版本的前提下,只需要在项目pom文件的dependencies元素直接引入.
创建工程时,只需添加SQL模块的jdbc API
、MySQL Driver
即可,在配置文件中配置数据源信息,就可以获取到数据库的连接。
并且数据源默认使用的是HiKari
。数据源信息是从DataSourceProperties
获取的
2.1 引入JDBC的Starter
spring-boot-starter-jdbc
spring-boot-starter-jdbc
提供了数据源配置、事务管理、数据访问等等功能,而对于不同类型的数据库,需要提供不同的驱动实现,才能更加简单地通过驱动实现根据连接URL、用户口令等属性直接连接数据库(或者说获取数据库的连接),因此对于不同类型的数据库,需要引入不同的驱动包依赖。
pom.xml
<!--引入 SpringBoot 的 jdbc模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<!-- 如果是使用log4j2,则要排除SpringBoot 默认的logback 日志框架-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
spring-boot-starter-jdbc模块(间接引入spring-core、spring-beans、spring-tx)、spring-boot-starter和HikariCP三个依赖。
引入spring-boot-starter-jdbc模块后,SpringBoot是自动配置了jdbcTamplate
的,可以使用jdbcTamplate
来操作数据查询。
2.2 引入连接数据库的驱动
mysql-connector-java
对于MySQL而言,需要引入mysql-connector-java
,
其中有数据库抽象驱动接口java.sql.Driver
的实现类:
- 对于mysql-connector-java而言,常用的实现是
com.mysql.cj.jdbc.Driver
(MySQL8.x版本)。
pom.xml
<!--mysql8 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2.3 配置数据源(默认HikariCP)
springboot2.x默认是用
com.zaxxer.hikari.HikariDataSource
作为数据源。
2.0以下默认采用的是org.apache.tomcat.jdbc.pool.DataSource
作为数据源。
springboot2.x的spring-boot-starter-jdbc模块 默认使用
HikariCP
作为数据库的连接池
HikariCP
,也就是Hikari Connection Pool,Hikari连接池。HikariCP的作者是日本人,而Hikari是日语,意义和light相近,也就是”光”。Simplicity is prerequisite for reliability(简单是可靠的先决条件)是HikariCP的设计理念,他是一款代码精悍的高性能连接池框架,被Spring项目选中作为内建默认连接池,值得信赖。
由于Spring Boot的自动化配置机制,大部分对于数据源的配置都可以通过配置参数的方式去改变。
在Spring Boot自动化配置中,对于数据源的配置可以分为两类:
-
通用配置: 以
spring.datasource.*
的形式存在,主要是对一些即使使用不同数据源也都需要配置的一些常规内容。比如:数据库链接地址、用户名、密码等。通常就这些配置:
# 注意MySQL8.x需要指定服务时区属性 spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true spring.datasource.username=xxx spring.datasource.password=xxx # 注意MySQL8.x的驱动 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
-
数据源连接池配置:以
spring.datasource.<数据源名称>.*
的形式存在。比如:
Hikari
的配置参数就是spring.datasource.hikari.*
形式。注意,需要指明数据源的类型:
spring.datasource.type: com.zaxxer.hikari.HikariDataSource
下面这个是我们最常用的几个配置项及对应说明:
# 连接池中允许的最大连接数。缺省值:10;推荐的公式:(cpu核心数*2+磁盘数) maximum-pool-size: 15 # 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟 idle-timeout: 600000 # 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:30秒 connection-timeout: 30000 # 连接只读数据库时配置为true, 保证安全 read-only: false # 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟,建议设置比数据库超时时长少30秒,参考MySQL wait_timeout参数(show variables like '%timeout%';) max-lifetime: 1800000
2.3.1 数据源完整配置项表
name | 描述 | 构造器默认值 | 默认配置validate之后的值 | validate重置 |
---|---|---|---|---|
autoCommit | 自动提交从池中返回的连接 | TRUE | TRUE | – |
connectionTimeout | 等待来自池的连接的最大毫秒数 | SECONDS.toMillis(30) = 30000 | 30000 | 如果小于250毫秒,则被重置回30秒 |
idleTimeout | 连接允许在池中闲置的最长时间 | MINUTES.toMillis(10) = 600000 | 600000 | 如果idleTimeout+1秒>maxLifetime 且 maxLifetime>0,则会被重置为0(代表永远不会退出);如果idleTimeout!=0且小于10秒,则会被重置为10秒 |
maxLifetime | 池中连接最长生命周期 | MINUTES.toMillis(30) = 1800000 | 1800000 | 如果不等于0且小于30秒则会被重置回30分钟 |
connectionTestQuery | 如果您的驱动程序支持JDBC4,我们强烈建议您不要设置此属性 | null | null | – |
minimumIdle | 池中维护的最小空闲连接数 | -1 | 10 | minIdle<0或者minIdle>maxPoolSize,则被重置为maxPoolSize |
maximumPoolSize | 池中最大连接数,包括闲置和使用中的连接 | -1 | 10 | 如果maxPoolSize小于1,则会被重置。当minIdle<=0被重置为DEFAULT_POOL_SIZE则为10;如果minIdle>0则重置为minIdle的值 |
metricRegistry | 该属性允许您指定一个 Codahale / Dropwizard MetricRegistry 的实例,供池使用以记录各种指标 | null | null | – |
healthCheckRegistry | 该属性允许您指定池使用的Codahale / Dropwizard HealthCheckRegistry的实例来报告当前健康信息 | null | null | – |
poolName | 连接池的用户定义名称,主要出现在日志记录和JMX管理控制台中以识别池和池配置 | null | HikariPool-1 | – |
initializationFailTimeout | 如果池无法成功初始化连接,则此属性控制池是否将 fail fast | 1 | 1 | – |
isolateInternalQueries | 是否在其自己的事务中隔离内部池查询,例如连接活动测试 | FALSE | FALSE | – |
allowPoolSuspension | 控制池是否可以通过JMX暂停和恢复 | FALSE | FALSE | – |
readOnly | 从池中获取的连接是否默认处于只读模式 | FALSE | FALSE | – |
registerMbeans | 是否注册JMX管理Bean(MBeans) | FALSE | FALSE | – |
catalog | 为支持 catalog 概念的数据库设置默认 catalog | driver default | null | |
connectionInitSql | 该属性设置一个SQL语句,在将每个新连接创建后,将其添加到池中之前执行该语句。 | null | null | – |
driverClassName | HikariCP将尝试通过仅基于jdbcUrl的DriverManager解析驱动程序,但对于一些较旧的驱动程序,还必须指定driverClassName | null | null | – |
transactionIsolation | 控制从池返回的连接的默认事务隔离级别 | null | null | – |
validationTimeout | 连接将被测试活动的最大时间量 | SECONDS.toMillis(5) = 5000 | 5000 | 如果小于250毫秒,则会被重置回5秒 |
leakDetectionThreshold | 记录消息之前连接可能离开池的时间量,表示可能的连接泄漏 | 0 | 0 | 如果大于0且不是单元测试,则进一步判断:(leakDetectionThreshold < SECONDS.toMillis(2) or (leakDetectionThreshold > maxLifetime && maxLifetime > 0),会被重置为0 . 即如果要生效则必须>0,而且不能小于2秒,而且当maxLifetime > 0时不能大于maxLifetime |
dataSource | 这个属性允许你直接设置数据源的实例被池包装,而不是让HikariCP通过反射来构造它 | null | null | – |
schema | 该属性为支持模式概念的数据库设置默认模式 | driver default | null | – |
threadFactory | 此属性允许您设置将用于创建池使用的所有线程的java.util.concurrent.ThreadFactory的实例 | null | null | – |
scheduledExecutor | 此属性允许您设置将用于各种内部计划任务的java.util.concurrent.ScheduledExecutorService实例 | null | null | – |
2.3.2 使用其他数据源(Druid)
Druid(github)
Druid官网
Druid Spring Boot Starter
Druid的中文说明
要禁用默认的数据源,改成Durid提供的数据源, 引入Druid数据源需要额外添加依赖
Druid的最强大之处在于它有着强大的监控,可以监控我们发送到数据库的所有sql语句,
方便我们后期排插错误
当我们需要使用其他数据库连接池时:
-
排除默认的数据源
-
导入相关的jar包
-
配置数据源信息
(1)排除默认数据源
<!--配置默认数据源 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<!-- 排除默认的 HikariCP 数据源 -->
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</exclusion>
</exclusions>
</dependency>
(2)引用 阿里巴巴的Druid数据源
<!-- 引用阿里巴巴的druid数据源 -->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
(3)配置Druid数据源
Druid Spring Boot Starter 配置属性的名称完全遵照 Druid;
-
我们可以通过 Spring Boot 配置文件(application.yml / application.properties)来配置Druid数据库连接池和监控,如果没有配置则使用默认值。具体配置可以参考Druid Spring Boot Starter
-
DruidDataSource
内提供 setter方法 的可配置属性都将被支持。你可以参考WIKI文档或通过IDE输入提示来进行配置。配置文件的格式你可以选择*.properties
或*.yml
,效果是一样的,在配置较多的情况下推荐使用*.yml
。 -
也可以自己写Druid的配置文件和@Configuration来配置;
这里我是自己定制的Druid的配置文件,在DruidDataSource
里面 配置监控中心:
@Configuration
@ConditionalOnClass({DruidDataSource.class})
@ConditionalOnMissingBean({DataSource.class})
@ConditionalOnProperty(
name = {"spring.datasource.type"},
havingValue = "com.alibaba.druid.pool.DruidDataSource"
)
@PropertySource(value = "classpath:druid.properties")
public class DruidConfig {
/**
* 注册 Druid 数据源
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource() {
return new DruidDataSource();
}
/**
* 配置 监控服务器
* @return 返回监控注册的servlet对象
*/
@Bean
public ServletRegistrationBean statViewServlet() {
//
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// 添加IP白名单
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
// 添加IP黑名单,当白名单和黑名单重复时,黑名单优先级更高
servletRegistrationBean.addInitParameter("deny", "127.0.0.1");
// 添加控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername", "123");
servletRegistrationBean.addInitParameter("loginPassword", "123");
// 是否能够重置数据
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
/**
* 配置服务过滤器
* @return 返回过滤器配置对象
*/
@Bean
public FilterRegistrationBean statFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
// 添加过滤规则
filterRegistrationBean.addUrlPatterns("/*");
// 忽略过滤格式
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,");
return filterRegistrationBean;
}
}
配置完后我们启动SpringBoot程序访问:
http://localhost:8080/druid/ 就可以来到我们的登录页面,登录之后,就是我们上面添加的控制台,我们可以在上面很好的看到运行状况。
3 数据源的自动配置原理(以hikari为例)
3.1 单元测试及其结果
1. 单元测试
@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootJdbcDemoApplicationTests {
@Qualifier("dataSource")
@Autowired
private DataSource dataSource;
@Test
void contextLoads() throws SQLException {
Connection connection = null;
try {
// 获取连接
connection = dataSource.getConnection();
// 打印 数据源
System.out.println("【dataSourceClass=】" + dataSource.getClass());
// 打印 连接对象
System.out.println("【connectionClass=】" + connection.getClass());
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
// 断言,连接不可以为空,才关闭连接
assert connection != null;
connection.close();
}
}
}
2. 运行结果
14:49:28.092 [main] INFO com.springboot.template.SpringbootJdbcDemoApplicationTests - Started SpringbootJdbcDemoApplicationTests in 1.818 seconds (JVM running for 3.051)
14:49:28.248 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
14:49:29.375 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
// 单元测试的打印结果
【dataSourceClass=】class com.zaxxer.hikari.HikariDataSource
【connectionClass=】class com.zaxxer.hikari.pool.HikariProxyConnection
14:49:29.400 [SpringContextShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
14:49:29.407 [SpringContextShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
14:49:29.407 [SpringContextShutdownHook] INFO org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor - Shutting down ExecutorService 'applicationTaskExecutor'
Disconnected from the target VM, address: '127.0.0.1:57273', transport: 'socket'
可以看到, springboot2.x 默认数据源使用的是hikariDatasorece
。
3.2 原理解析
相关配置类,都可以在
org.springframework.boot.autoconfigure.jdbc
下找到数据源的相关属性配置都是在
DataSourceProperties
中;
实际的数据源导入配置都在DataSourceConfiguration
中;
实际的数据源的自动配置都在DataSourceAutoConfiguration
中。
3.2.1 数据源的导入配置
org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration
SpringBoot支持的数据源,有如下几个:
// SpringBoot默认支持的三个数据源
org.apache.commons.dbcp2.BasicDataSource: dbcp2的数据源
com.zaxxer.hikari.HikariDataSource: hikari的数据源(默认的)
org.apache.tomcat.jdbc.pool.DataSource: tomcat的数据源
// 使用其他的数据库连接池:例如Druid
Generic: 其他的数据源
3.2.1.1 hikari的数据源的配置原理
org.springframework.boot.jdbc.DataSourceConfiguration
根据项目导入的依赖和配置文件配置的限定,选择性的将SpringBoot支持的指定数据源,注册到Spring的IOC容器中。
1> 静态成员内部类 Hikari
abstract class DataSourceConfiguration {
// 略
// 配置类,以下几个注解同时成立,@Configuration才生效
@Configuration(
proxyBeanMethods = false
)
// 在当前类路径下必须存在 HikariDataSource.class, 配置类才被注册
@ConditionalOnClass({HikariDataSource.class})
// 仅在该注解规定的类DataSource不存在于 spring容器中时,配置类才被注册,避免生成多个dataSource
@ConditionalOnMissingBean({DataSource.class})
// 控制配置类是否应该注册,
// 通过其两个属性name以及havingValue来实现的,其中name用来从application.properties中读取某个属性值。
// 如果该值为空,则返回false;
// 如果值不为空,则将该值与havingValue指定的值进行比较,如果一样则返回true;否则返回false。
// 如果返回值为false,则该configuration不生效;为true则生效。
@ConditionalOnProperty(
name = {"spring.datasource.type"},
havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true
)
static class Hikari {
Hikari() {
}
@Bean
@ConfigurationProperties(
// 注入配置文件的属性配置,如初始连接数,最大最小等的前缀
prefix = "spring.datasource.hikari"
)
HikariDataSource dataSource(DataSourceProperties properties) {
// 创建 HikariDataSource ,绑定 相关属性信息
HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
// 设置连接池(数据源) 名称
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
// 创建一个DataSourceBuilder对象,使用DataSourceBuilder创建数据源,利用反射创建指定type的数据源,并且绑定相关属性
// 返回创建好的数据源
return properties.initializeDataSourceBuilder().type(type).build();
}
// 略
}
2> HikariDataSource (Hikari 数据源)
org.springframework.boot.jdbc.HikariDataSource
实现了 HikariConfig
接口,这个就扣主要都是 Hikari 数据源的 配置信息;
继承 javax.sql.DataSource
,重写了 getConnection / setConnetion方法。
public class HikariDataSource extends HikariConfig implements DataSource, Closeable {
private final AtomicBoolean isShutdown = new AtomicBoolean();
private final HikariPool fastPathPool;
// Hikari 连接池,数据库连接由连接池提供
private volatile HikariPool pool;
// setter、getter 等略
}
可以看出,Hikari数据源的配置信息主要从两个类中读取:
-
一个是
org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
, -
另一个则是本类HikariDataSource的父类
com.zaxxer.hikari.HikariConfig
。
3.2.1.2 数据源构建器(DataSourceBuilder)
org.springframework.boot.jdbc.DataSourceBuilder
上述使用到数据源构建器(DataSourceBuilder)类,创建 DataSource
其中的build
方法,利用反射创建指定type的数据源,并且绑定相关属性,返回一个DataSource
对象。
// 这里的 泛型T 就是 HikariDataSource
public final class DataSourceBuilder<T extends DataSource> {
// 略
// SpringBoot 支持的数据源 type
private static final String[] DATA_SOURCE_TYPE_NAMES =
new String[]{"com.zaxxer.hikari.HikariDataSource",
"org.apache.tomcat.jdbc.pool.DataSource",
"org.apache.commons.dbcp2.BasicDataSource"};
public T build() {
// 获取 HikariDataSource 的Class 对象
Class<? extends DataSource> type = this.getType();
// 利用反射,获取指定类型的数据源
DataSource result = (DataSource)BeanUtils.instantiateClass(type);
// 设置连接数据库驱的动全类名,用于绑定到数据源
this.maybeGetDriverClassName();
// 绑定相关属性到 数据源
// 相关属性就是 :
// username、password、url、driverClassName
this.bind(result);
return result;
}
// 略
}
3.2.1.3 hikari数据源的自动配置
org.springframework.boot.jdbc.DataSourceAutoConfiguration
DataSourceAutoConfiguration
是springboot加载数据源的核心配置类;
Spring Boot启动后会调用它,配置数据源。
DataSourceAutoConfiguration中有两个重要的嵌套类:
-
EmbeddedDatabaseConfiguration
: 配置Spring Boot的嵌入示DataSource。除了Maven中加入相应的Driver,可以不做其他额外配置就能使用。从EmbeddedDatabaseType类可以看出,Spring Boot的内嵌DataSource支持 HSQL,H2,DERBY 这三种数据库。 -
PooledDataSourceConfiguration
: 配置Spring Boot默认支持的一些DataSource。从org.springframework.boot.jdbc.DataSourceBuilder
中可以看出,Spring Boot(2.x)只支持com.zaxxer.hikari.HikariDataSource
,org.apache.tomcat.jdbc.pool.DataSource
,org.apache.commons.dbcp2.BasicDataSource
。
以上两个配置类都会调用ConfigurationClassParser
类的processConfigurationClass
方法来处理
// 数据源的自动配置类
@Configuration(
proxyBeanMethods = false
)
// 在当前类路径下存在DataSource.class,EmbeddedDatabaseType.class 时,这个配置类才注入到IOC容器
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
// 仅在该注解规定的类ConnectionFactory不存在于 spring容器中时,配置类才被注册
@ConditionalOnMissingBean(
type = {"io.r2dbc.spi.ConnectionFactory"}
)
// 把 @ConfigurationProperties 注解的 DataSourceProperties类,注入到IOC容器
// 这就是自动配置的主要原因之一
@EnableConfigurationProperties({DataSourceProperties.class})
// 在运用的时候,获取该注解标识的类,注入到IOC容器
// DataSourcePoolMetadataProvidersConfiguration.class 自动装配每个数据源的元数据
// DataSourceInitializationConfiguration.class 数据源的初始化配置,
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
// 略
// 这里就使用到了上述的 DataSourceConfiguration抽象类,注入数据源(内部静态类)到IOC容器
// 数据源导入的配置类
@Configuration(
proxyBeanMethods = false
)
// 配置有spring.datasource.type并且其值不是false 时生效,
// 如果在当前类路径下存在:
// org.apache.tomcat.jdbc.pool.DataSource
// com.zaxxer.hikari.HikariDataSource
// org.apache.commons.dbcp.BasicDataSource
// org.apache.commons.dbcp2.BasicDataSource
// 任意一个时,这个配置类才可以注入到IOC容器
@Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})
// IOC容器中不存在DataSource,XADataSource类型的bean时,这个配置类才可以注入到IOC容器
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
// 在运用的时候,将DataSourceConfiguration配置类中,符合条件的数据源,注入到IOC容器
// 就是根据条件限制,依次导入Tomcat,Hikari,Dbcp, Dbcp2的配置
// 如果没有,就检查有没有 Generic.class 其他的数据源
// 还没有,就是使用内嵌的数据源
@Import({Hikari.class, Tomcat.class, Dbcp2.class, Generic.class, DataSourceJmxConfiguration.class})
protected static class PooledDataSourceConfiguration {
protected PooledDataSourceConfiguration() {
}
}
// 略
}
3.2.1.4 使用其他数据源
org.springframework.boot.jdbc.DataSourceConfiguration
可以使用 spring.datasource.type
指定自定义的数据源类型,值要使用数据源全类名。
默认情况下,它是从类路径自动检测的,源码如下:
abstract class DataSourceConfiguration {
// 略
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingBean({DataSource.class})
@ConditionalOnProperty(
name = {"spring.datasource.type"}
)
static class Generic {
Generic() {
}
@Bean
DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
// 略
}
可以看到,只要配置文件中,只配置了其他数据源,导入了其他数据源的依赖,那么,这个其他数据源就会被生成使用,转而会生成我们配置使用的数据源。
SpringBoot默认使用的数据源Hikari,因为不满足限定条件,而不会生成使用。
3.2.1.5 数据源的初始化
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration
这个类,在DataSourceConfiguration
中,被注入到IOC容器;
主要作用是,数据源的初始化配置。
@Configuration(
proxyBeanMethods = false
)
// 运行时,注入这些标注的类
@Import({DataSourceInitializerInvoker.class, DataSourceInitializationConfiguration.Registrar.class})
class DataSourceInitializationConfiguration {
// 略
// 这个内部类
// 主要是给 数据源bean,注册后置处理器
// dataSourceInitializerPostProcessor
static class Registrar implements ImportBeanDefinitionRegistrar {
private static final String BEAN_NAME = "dataSourceInitializerPostProcessor";
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition("dataSourceInitializerPostProcessor")) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DataSourceInitializerPostProcessor.class);
beanDefinition.setRole(2);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition("dataSourceInitializerPostProcessor", beanDefinition);
}
}
}
}
DataSourceInitializerInvoke
类
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoke
DataSourceInitializerInvoke
,实现了ApplicationListener接口,实际上就是一个 监听器(ApplicationListener);
这里它监听的是 DataSourceSchemaCreatedEvent
事件,就是建表的事件。
作用:
-
初始化的时候帮我们运行schene-文件(建表),和 data-(数据);
-
afterPropertiesSet
方法,初始化bean的时候执行,可以针对某个具体的bean进行配置
class DataSourceInitializerInvoker implements ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean {
// 略
public void afterPropertiesSet() {
// 从容器中拿到数据源对象初始化器
DataSourceInitializer initializer = this.getDataSourceInitializer();
if (initializer != null) {
// createSchema()->runScripts(),运行建表语句
boolean schemaCreated = this.dataSourceInitializer.createSchema();
// 若建表语句创建成功
if (schemaCreated) {
// initialize()->initSchema(),运行插入数据的语句
this.initialize(initializer);
}
}
}
// DataSourceSchemaCreatedEvent 发生时,触发此方法
public void onApplicationEvent(DataSourceSchemaCreatedEvent event) {
DataSourceInitializer initializer = this.getDataSourceInitializer();
if (!this.initialized && initializer != null) {
// 运行插入数据的语句
initializer.initSchema();
this.initialized = true;
}
}
// 略
}
DataSourceInitializer
类
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer
默认只需要在类路径下,将文件命名为:
# schema‐*.sql、data‐*.sql
# 默认规则:schema.sql,schema‐all.sql;
# 可以使用自定义位置:
schema:
‐ classpath:department.sql
就可以在SpringBoot启动后,执行sql脚本,在数据库,建表,更新数据。
部分源码:
class DataSourceInitializer {
// 略
// 建表
boolean createSchema() {
List<Resource> scripts = this.getScripts("spring.datasource.schema", this.properties.getSchema(), "schema");
// 这里要在 application.properties 配置文件中配置,才会执行类路径下的sql脚本
// spring.datasource.initialization-mode: always
if (!scripts.isEmpty()) {
if (!this.isEnabled()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return false;
}
String username = this.properties.getSchemaUsername();
String password = this.properties.getSchemaPassword();
this.runScripts(scripts, username, password);
}
return !scripts.isEmpty();
}
// 更新数据
void initSchema() {
List<Resource> scripts = this.getScripts("spring.datasource.data", this.properties.getData(), "data");
if (!scripts.isEmpty()) {
if (!this.isEnabled()) {
logger.debug("Initialization disabled (not running data scripts)");
return;
}
String username = this.properties.getDataUsername();
String password = this.properties.getDataPassword();
this.runScripts(scripts, username, password);
}
}
// 获取 sql 脚本在类路径下的 位置
// 默认是 classpath:schema-all.sql 和 classpath:schema.sql
private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {
if (resources != null) {
return this.getResources(propertyName, resources, true);
} else {
// platform 默认是 all
String platform = this.properties.getPlatform();
List<String> fallbackResources = new ArrayList();
// fallback 默认是 schema
// classpath:schema-all.sql
fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");
// classpath:schema.sql
fallbackResources.add("classpath*:" + fallback + ".sql");
return this.getResources(propertyName, fallbackResources, false);
}
}
// 略
}
看getScripts源码,它还会加载schema-${platform}.sql文件,或者data-${platform}.sql文件,其中platform就是spring.datasource.platform的值;当然,也可以自定义sql脚本的名称,不过,需要在类路径下,配置sql脚本的位置信息。
SpringBoot2.x 使用sql文件初始化的一些注意事项:
- Springboot启动成功后,
下次要将sql脚本内的建表语句注释,
不然会报错:数据库表已经存在,无法创建
nested exception is java.sql.SQLSyntaxErrorException: Table 'admin' already exists
- 在application配置文件加上
spring.datasource.initialization-mode=always
,否则不生效;
spring.datasource.initialization-mode
初始化模式(springboot2.0),其中有三个值,
always : 为始终执行初始化,
embedded : 只初始化内存数据库(默认值),如h2等,
never : 为不执行初始化
- sql脚本中尽量不要使用注释,否则会把紧跟的sql语句解释为注释,而不执行。
因为SpringBoot在启动时,只有检测到spring.datasource.initialization-mode=always
配置;
然后,再检测spring.datasource.schema
/ spring.datasource.data
,且配置的sql脚本语句不为空,才会去执行sql脚本。
application.yml
spring:
datasource:
platform: mysql # 数据库类型
initialization-mode: always
schema:
- classpath:sql/user.sql
data:
- classpath:sql/userData.sql
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/69762.html