使用方式
application.yml
# 数据源配置
spring:
sql:
init:
# mode: always - Spring Boot应用启动时始终执行数据库初始化
# mode: embedded - 只初始化内存数据库,比如H2数据库
# mode: never: - 从不执行初始化数据库
mode: always
# 数据库平台内容配置项,主要有mysql、postgresql、oracle等
platform: mysql
# 创建表sql脚本的位置
schema-locations:
- classpath:/db/schema.sql
# sql数据的位置
#data-locations:
# - classpath:/db/data.sql
schema.sql
create table if not exists app_excel_record
(
id bigint unsigned auto_increment comment 'ID'
primary key,
file_name varchar(64) default '' not null comment '文件名称',
module varchar(64) default '' not null comment '模块',
create_time datetime null comment '创建时间',
update_time datetime null comment '更新时间',
create_by int null comment '创建人',
file_url varchar(200) null comment '文件绝对路径',
record_status int null comment '导出状态',
result_info int null comment '导出结果'
) comment '导出记录表';
效果:系统启动会执行app_excel_record的创表语句。
源码解析
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@AutoConfigureBefore(SqlInitializationAutoConfiguration.class)
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.InitializationSpecificCredentialsDataSourceInitializationConfiguration.class,
DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
}
SqlInitializationAutoConfiguration
SqlInitializationAutoConfiguration该类注入会判断SqlInitializationModeCondition条件是否符合,当spring.sql.init.mode不等于never,会注入到Spring容器中。
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter({ R2dbcAutoConfiguration.class, DataSourceAutoConfiguration.class })
@EnableConfigurationProperties(SqlInitializationProperties.class)
@Import({ DatabaseInitializationDependencyConfigurer.class, R2dbcInitializationConfiguration.class,
DataSourceInitializationConfiguration.class })
@ConditionalOnProperty(prefix = "spring.sql.init", name = "enabled", matchIfMissing = true)
@Conditional(SqlInitializationModeCondition.class)
public class SqlInitializationAutoConfiguration {
static class SqlInitializationModeCondition extends NoneNestedConditions {
SqlInitializationModeCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(prefix = "spring.sql.init", name = "mode", havingValue = "never")
static class ModeIsNever {
}
}
}
DataSourceInitializationConfiguration
DataSourceInitializationConfiguration注入了SqlDataSourceScriptDatabaseInitializer
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean({ SqlDataSourceScriptDatabaseInitializer.class, SqlR2dbcScriptDatabaseInitializer.class })
@ConditionalOnSingleCandidate(DataSource.class)
@ConditionalOnClass(DatabasePopulator.class)
class DataSourceInitializationConfiguration {
@Bean
SqlDataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource,
SqlInitializationProperties properties) {
return new SqlDataSourceScriptDatabaseInitializer(
determineDataSource(dataSource, properties.getUsername(), properties.getPassword()), properties);
}
private static DataSource determineDataSource(DataSource dataSource, String username, String password) {
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
return DataSourceBuilder.derivedFrom(dataSource).username(username).password(password)
.type(SimpleDriverDataSource.class).build();
}
return dataSource;
}
}
SqlDataSourceScriptDatabaseInitializer
SqlDataSourceScriptDatabaseInitializer继承了AbstractScriptDatabaseInitializer,AbstractScriptDatabaseInitializer是实现了InitializingBean接口的。所以在启动的时候会执行AbstractScriptDatabaseInitializer#afterPropertiesSet。
public void afterPropertiesSet() throws Exception {
this.initializeDatabase();
}
public boolean initializeDatabase() {
AbstractScriptDatabaseInitializer.ScriptLocationResolver locationResolver = new AbstractScriptDatabaseInitializer.ScriptLocationResolver(this.resourceLoader);
boolean initialized = this.applySchemaScripts(locationResolver);
return this.applyDataScripts(locationResolver) || initialized;
}
AbstractScriptDatabaseInitializer#applySchemaScripts。
private boolean applySchemaScripts(AbstractScriptDatabaseInitializer.ScriptLocationResolver locationResolver) {
return this.applyScripts(this.settings.getSchemaLocations(), "schema", locationResolver);
}
private boolean applyDataScripts(AbstractScriptDatabaseInitializer.ScriptLocationResolver locationResolver) {
return this.applyScripts(this.settings.getDataLocations(), "data", locationResolver);
}
private boolean applyScripts(List<String> locations, String type, AbstractScriptDatabaseInitializer.ScriptLocationResolver locationResolver) {
List<Resource> scripts = this.getScripts(locations, type, locationResolver);
if (!scripts.isEmpty() && this.isEnabled()) {
this.runScripts(scripts);
return true;
} else {
return false;
}
}
private void runScripts(List<Resource> resources) {
this.runScripts(resources, this.settings.isContinueOnError(), this.settings.getSeparator(), this.settings.getEncoding());
}
protected abstract void runScripts(List<Resource> resources, boolean continueOnError, String separator, Charset encoding);
DataSourceScriptDatabaseInitializer#runScripts,执行脚本
protected void runScripts(List<Resource> resources, boolean continueOnError, String separator, Charset encoding) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setContinueOnError(continueOnError);
populator.setSeparator(separator);
if (encoding != null) {
populator.setSqlScriptEncoding(encoding.name());
}
Iterator var6 = resources.iterator();
while(var6.hasNext()) {
Resource resource = (Resource)var6.next();
populator.addScript(resource);
}
this.customize(populator);
DatabasePopulatorUtils.execute(populator, this.dataSource);
}
实战
https://gitee.com/charles_ruan/easy-export