SpringBoot-11-JDBC
SpringData简介
对于数据访问层,无论是 SQL(关系型数据库) 还是 NOSQL(非关系型数据库),Spring Boot 底层都是采用 Spring Data 的方式进行统一处理。
Spring Data 官网
查看数据库相关的启动器 :官方文档
整合JDBC
1.创建一个新项目:springboot-04-jdbc,引入相应的模块
2.项目搭建好后,查看spring Boot给我们导入的场景启动器和依赖
3.在全局(appication.yaml)配置文件编写数据源
#数据源连接信息
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: root
- 测试
@SpringBootTest
class Springboot04JdbcApplicationTests {
//自动装配数据源
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
//查看数据源
System.out.println(dataSource.getClass());
//获得连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
//关闭连接
connection.close();
}
}
运行结果
Spring Boot 默认使用 HikariCP 作为其数据源,对数据库的访问。
可以通过修改配置文件来修改数据源
实现如下:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: root
# 修改默认数据源
type: org.springframework.jdbc.datasource.DriverManagerDataSource
运行结果
Spring Boot版本2.2.5默认使用HikariDataSource作为数据源,而以前版本 如Spring Boot版本1.5默认使用 org.apache.tomcat.jdbc.pool.DataSource 作为数据源。
HikariDataSource 号称 Java WEB 当前速度最快的数据源,相比于传统的 C3P0 、DBCP、Tomcat jdbc 等连接池更加优秀;
可以spring.datasource.type 指定自定义的数据源类型,值为 要使用的连接池实现的完全限定名。
分析原理
SpringBoot 几乎所有的默认配置都是通过配置类XxxAutoConfiguration进行配置,Spring Boot 的数据源也不例外,它的自动配置类是DataSourceAutoConfiguration
在/META-INF/spring.factories文件找到DataSourceAutoConfiguration配置类
EmbeddedDataSourceConfiguration 向容器中添加了一个 Spring Boot 内嵌的数据源,该数据源支持 HSQL,H2 和 DERBY 三种数据库
DataSourceAutoConfiguration共包括5个内部配置类
- EmbeddedDatabaseCondition
- PooledDataSourceAvailableCondition
- PooledDataSourceConfiguration
- PooledDataSourceConfiguration(池化数据源自动配置类)
- EmbeddedDatabaseConfiguration(内嵌数据源自动配置类)
其中,PooledDataSourceConfiguration和EmbeddedDatabaseConfiguration为使用了@Configuration注解的自动配置类,其余三个为限定条件类。
EmbeddedDatabaseConfiguration
@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class)
//容器汇总没有数据源的配置类下生效
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {
}
EmbeddedDatabaseConfiguration是内嵌的数据源配置类,类中没有任何方法实现,主要的功能是通过@Import注解引入EmbeddedDataSourceConfiguration类实现的
@Import(EmbeddedDataSourceConfiguration.class)
点击EmbeddedDataSourceConfiguration
@Configuration(proxyBeanMethods = false)
//开启配置属性,绑定DataSourceProperties类
@EnableConfigurationProperties(DataSourceProperties.class)
public class EmbeddedDataSourceConfiguration implements BeanClassLoaderAware {
private ClassLoader classLoader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
//向容器中添加 Spring Boot 内嵌的数据源
@Bean(destroyMethod = "shutdown")
public EmbeddedDatabase dataSource(DataSourceProperties properties) {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseConnection.get(this.classLoader).getType())
.setName(properties.determineDatabaseName()).build();
}
}
通过上面的分析,可知自动配置类 EmbeddedDatabaseConfiguration 的作用是向容器中添加一个内嵌的数据源(DataSource),但这是有条件限制的。
条件限制:
@Conditional(EmbeddedDatabaseCondition.class)
protected static class EmbeddedDatabaseConfiguration {
}
EmbeddedDatabaseConfiguration类上使用一个@Conditional注解,该注解使用了 DataSourceAutoConfiguration 的内部限制条件类 EmbeddedDatabaseCondition 来进行条件判断。
说明:
EmbeddedDatabaseCondition主要用来检测容器中是否已经存在池化数据源(PoolDataSource)。若容器中存在池化数据源时,则EmbeddedDatabaseConfiguration不能被实例化。只有当容器不存在池化数据源时,EmbeddedDatabaseConfiguraion才能被实例化,才能向容器中添加内嵌数据源(EmbeddedDataSource)
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition("EmbeddedDataSource");
if (hasDataSourceUrlProperty(context)) {
return ConditionOutcome.noMatch(message.because(DATASOURCE_URL_PROPERTY + " is set"));
}
if (anyMatches(context, metadata, this.pooledCondition)) {
return ConditionOutcome.noMatch(message.foundExactly("supported pooled data source"));
}
EmbeddedDatabaseType type = EmbeddedDatabaseConnection.get(context.getClassLoader()).getType();
if (type == null) {
return ConditionOutcome.noMatch(message.didNotFind("embedded database").atAll());
}
return ConditionOutcome.match(message.found("embedded database").items(type));
}
PooledDataSourceConfiguration
@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 {
}
-
PooledDataSourceConfiguration 是池化数据源的自动配置类,通过@Conditional注解可知,该注解使用DataSourceAutoConfiguration的内部限制条件类PooledDataSourceCondition来进行条件判断。
-
PooledDataSourceCondition 与 EmbeddedDatabaseCondition 一样,也是用来检测容器中是否已经存在池化数据源的,但不同的是,PooledDataSourcceConfiguraion只有当容器存在池化数据源时,才可以被实例化,才可以向容器中添加池化数据源。
-
与 EmbeddedDatabaseConfiguration 一样,PooledDataSourceConfiguration 类中也没有任何的方法实现,它的所有功能都是通过 @Import 注解引入其他的类实现的。
-
PooledDataSourceConfiguration 通过 @Import 注解引入了 Hikari、Tomcat、Dbcp2、OracleUcp 和 Generic 五个数据源配置类,它们都是 DataSourceConfiguration 的内部类,且它们的功能类似,都是向容器中添加指定的数据源。
以 Hikari 为例进行分析
@Configuration(proxyBeanMethods = false)
//表示这是一个配置类
@ConditionalOnClass(HikariDataSource.class)
//表示必须类路径下存在HikariDataSource类,这个配置类才会被实例化
//HikariDataSource 类是由 spring- boot-starter-jdbc 默认将其引入的,因此只要我们在 pom.xml 中引入了该 starter, Hikari 就会被实例化
@ConditionalOnMissingBean(DataSource.class)
//容器中没有DataSource这个类,才会被实例化
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
//Spring Boot配置文件时,配置了spring.datasource.type=com.zaxxer.hikari.HikariDataSource或者不配置spring.datasource.tyoe时,Hikari才会被实例化
static class Hikari {
@Bean
// 与配置文件名为spring.datasource.hikari下的所有属性绑定
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
Hikari类通过@Bean注解向容器中添加了HikariDataSource组件,该组件的实例对象是通过调用DataSourceConfiguration的createDataSource()方法得到的
createDataSource()
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
DataSourceProperties类
可以知道,DataSourceProperties和我们之前在全局配置下的spring.datasource的所有属性一一对应绑定在一起,DataSourceProperties里面包含数据库的连接数据库和释放数据库等操作/.
调用了DataSourceProperties的initializeDataSourceBuilder()初始化DataSourceBuilder
initializeDataSourceBuilder()
public DataSourceBuilder<?> initializeDataSourceBuilder() {
return DataSourceBuilder.create(getClassLoader()).type(getType()).driverClassName(determineDriverClassName())
.url(determineUrl()).username(determineUsername()).password(determinePassword());
}
initialDataSourceBuilder()方法通过调用DataSourceBulider的create()方法,将application.properties/yaml的配置,依次设置数据源类型、驱动类名、连接 url、 用户名和密码等信息。
sprIng.datasource.type默认是不用配置的。createDataSource() 方法在获取到回传回来的 DataSourceBuilder 对象后,还需要将其 type 属性再次设置为 HikariDataSourcee,并调用 DataSourceBuilder 的 build() 方法,完成 HikariDataSource 的初始化。
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
dataSource() 方法获得数据源对象,并设置了连接池的名字(name),注入到容器中。
总结:
- 用户没有配置数据源的情况,若容器中存在 HikariDataSource 类,则 Spring Boot 就会自动实例化 Hikari,并将其作为其数据源。
- Spring Boot 的 JDBC 场景启动器(spring-boot-starter-data-jdbc)通过 spring- boot-starter-jdbc 默认引入了 HikariCP 数据源(包含 HikariDataSource 类),因此 Spring Boot 默认使用 HikariCP 作为其数据源。