配置数据源
spring:
datasource:
password: root
url: jdbc:mysql://127.0.0.1:3306/supermarket?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
高版本驱动访问低版本mysql要选带cj的Driver,另外要加时区,不然报错,账号密码就输入你自己的账号密码就行,还有就是不要选带"-"的那个username和password,那个登不上去,
这样jdbc就配置好了,在springboot2.0以下的版本默认是用的tomcat的连接池的数据源org.apache.tomcat.jdbc.pool.DataSource.class
但是在2.0以上版本,默认是com.zaxxer.hikari.HikariDataSource这个数据源
可以参考DataSourceConfiguration这个类
abstract class DataSourceConfiguration {
@SuppressWarnings("unchecked")
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
/**
* Tomcat Pool DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
matchIfMissing = true)
static class Tomcat {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties,
org.apache.tomcat.jdbc.pool.DataSource.class);
DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
String validationQuery = databaseDriver.getValidationQuery();
if (validationQuery != null) {
dataSource.setTestOnBorrow(true);
dataSource.setValidationQuery(validationQuery);
}
return dataSource;
}
}
/**
* Hikari DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@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;
}
}
/**
* DBCP DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp2.BasicDataSource",
matchIfMissing = true)
static class Dbcp2 {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.dbcp2")
org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties) {
return createDataSource(properties, org.apache.commons.dbcp2.BasicDataSource.class);
}
}
/**
* Generic DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
}
数据源的相关配置都在dataSourceProperties这个类里面,我们看到@ConditionalOnProperty(name = “spring.datasource.type”)这里,可以知道在配置文件中我们使用spring.datasource.type也是可以指定数据源的,我么你自己指定的数据源被获取到DataSourceProperties的type属性中,然后这个DataSourceProperties调用initializeDataSourceBuilder方法获得已个数据源建造的类,然后调用build方法
public T build() {
Class<? extends DataSource> type = getType();
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
bind(result);
return (T) result;
}
在build方法里面同过反射的原理创建数据源,并且绑定相关属性
另外springboot提供了一个数据源自动配置类DataSourceConfiguration
DataSourceConfiguration
@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 {
我们看到引入了DataSourceInitializationConfiguration.class这个类
@Configuration(proxyBeanMethods = false)
@Import({ DataSourceInitializerInvoker.class, DataSourceInitializationConfiguration.Registrar.class })
class DataSourceInitializationConfiguration {
在这里又引入了DataSourceInitializerInvoker这个类
/**
* Bean to handle {@link DataSource} initialization by running {@literal schema-*.sql} on
* {@link InitializingBean#afterPropertiesSet()} and {@literal data-*.sql} SQL scripts on
* a {@link DataSourceSchemaCreatedEvent}.
*
* @author Stephane Nicoll
* @see DataSourceAutoConfiguration
*/
class DataSourceInitializerInvoker implements ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean {
我们发现invoke这个类实现了ApplicationListener接口,这里应该是数据源创建语句的监听,看上面的注释知道,这是springboot初始化的时候帮我们加载schema- *.sql的文件,还有data- *.sql的文件
,结合查阅相关文件,发现在springboot2.0里面没有了init这个方法,方法上有PostConstruct那个注解,这个方法只在springboot1.0里面有,在springboot2.0里面有这样一个方法
@Override
public void afterPropertiesSet() {
DataSourceInitializer initializer = getDataSourceInitializer();
if (initializer != null) {
boolean schemaCreated = this.dataSourceInitializer.createSchema();
if (schemaCreated) {
initialize(initializer);
}
}
}
@Override
public void onApplicationEvent(DataSourceSchemaCreatedEvent event) {
// NOTE the event can happen more than once and
// the event datasource is not used here
DataSourceInitializer initializer = getDataSourceInitializer();
if (!this.initialized && initializer != null) {
initializer.initSchema();
this.initialized = true;
}
}
这个afterPropertiesSet就是运行建表语句的方法,initialize里面是运行插入数据的方法,这个afterPropertiesSet就是在配置文件配置spring.datasource.initialization-mode: always之后,才可运行该方法
spring:
datasource:
password: root
url: jdbc:mysql://127.0.0.1:3306/supermarket?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
initialization-mode: always
schema:
- classpath: department.sql
afterPropertiesSet方法中可以看到调用的createSchema()方法,
在onApplicationEvent方法中调用的initSchema方法,上面那个方法是执行建表语句的,下面的方法是执行插入数据的sql语句的
/**
* Create the schema if necessary.
* @return {@code true} if the schema was created
* @see DataSourceProperties#getSchema()
*/
boolean createSchema() {
List<Resource> scripts = getScripts("spring.datasource.schema", this.properties.getSchema(), "schema");
if (!scripts.isEmpty()) {
if (!isEnabled()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return false;
}
String username = this.properties.getSchemaUsername();
String password = this.properties.getSchemaPassword();
runScripts(scripts, username, password);
}
return !scripts.isEmpty();
}
/**
* Initialize the schema if necessary.
* @see DataSourceProperties#getData()
*/
void initSchema() {
List<Resource> scripts = getScripts("spring.datasource.data", this.properties.getData(), "data");
if (!scripts.isEmpty()) {
if (!isEnabled()) {
logger.debug("Initialization disabled (not running data scripts)");
return;
}
String username = this.properties.getDataUsername();
String password = this.properties.getDataPassword();
runScripts(scripts, username, password);
}
}
,在这俩个方法中可以看到,都是调用的getScripts方法
private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {
if (resources != null) {
return getResources(propertyName, resources, true);
}
String platform = this.properties.getPlatform();
List<String> fallbackResources = new ArrayList<>();
fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");
fallbackResources.add("classpath*:" + fallback + ".sql");
return getResources(propertyName, fallbackResources, false);
}
通过看源码可以知道,springboot会默认的查找配置文件中是否配置了schema或是data属性的路径,如果我们没有配置该属性,那么springboot会默认的查找classpath下,某个包内的schema-all.sql或者schema.sql(data-all.sql或者data.sql)这个文件进行执行,