有些情况下我们需要在服务启动时执行一段SQL脚本,比如建立数据库,插入一些用户角色数据等,下面介绍两种方式。
方式1很简单,这种方式需要使用spring的数据源自动配置。
在yaml文件或properties文件中直接配置,以properties为例:
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/newframe
spring.datasource.schema=classpath:sql/newframe.sql,classpath:sql/demo.sql
spring.datasource.initialization-mode=always
注意使用springboot2.x必须加上spring.datasource.initialization-mode=always
,如果只初始化一次就够了,记得初始化后将always修改为never
重点介绍下方式2,在某些情况下,未使用springboot的数据源自动配置,有没有办法初始化执行脚本呢?这里介绍使用Mybatis的ScriptRunner初始化的方式。
......
#初始化脚本(多个以逗号分隔)
spring.datasource.druid.mysql1.schema=classpath:sql/newframe.sql
#是否开启脚本初始化,always-开启 never-不开启
spring.datasource.druid.mysql1.initialization-mode=never
......
@Bean(name = "dynamicDatasource")
public DataSource dynamicDataSource() throws SQLException {
String db = environment.getProperty("spring.datasource.names");
Map<Object, Object> targetDataSources = new LinkedHashMap<>(2 << 2);
Map<String, Object> enableAtomikosMap = context.getBeansWithAnnotation(EnableAtomikos.class);
if (db != null) {
List<String> dbNames = Arrays.asList(db.split(","));
if (CollectionUtil.isNotEmpty(dbNames)) {
for (String dbName : dbNames) {
//初始化数据源
//update by zqw 20210520 添加Atomikos支持
DruidDataSource dataSource = initOneDatasource(dbName);
if (dataSource != null) {
if (MapUtil.isNotEmpty(enableAtomikosMap)) {
//如果开启了Atomikos
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource((XADataSource) dataSource);
//必须设置uniqueResourceName
atomikosDataSourceBean.setUniqueResourceName(dbName);
targetDataSources.put(dbName, atomikosDataSourceBean);
} else {
//如果没开启Atomikos
targetDataSources.put(dbName, dataSource);
}
}
//运行初始化脚本 add by zqw 2021-12-26
String path = basePath + dbName + ".";
String value = environment.getProperty(path + "initialization-mode");
if (Objects.equals("always", value)) {
value = environment.getProperty(path + "schema");
if (StringUtils.isNotEmpty(value)) {
runSchema(dataSource, value, dbName);
}
}
}
}
}
//如果有shardingjdbc的配置,读取
Map<String, Object> enableShardingJdbcMap = context.getBeansWithAnnotation(EnableShardingJdbc.class);
if (CollectionUtil.isNotEmpty(enableShardingJdbcMap)) {
IShardingJdbcHandler shardingJdbcHandler = context.getBean(IShardingJdbcHandler.class);
if (shardingJdbcHandler != null) {
targetDataSources.putAll(shardingJdbcHandler.initShardingDatasource());
}
// DataSource dataSource = getShardingDatasource();
// targetDataSources.put("ds0_ds1", dataSource);
}
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//设置数据源
if (targetDataSources.size() > 0) {
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(targetDataSources.entrySet().iterator().next().getValue());
} else {
log.error("没有数据源,无法使用数据库!!!");
return null;
}
dynamicDataSource.afterPropertiesSet();
return dynamicDataSource;
}
其他的都可以忽略,只需要关注一下带有注释2021-12-26下面的一段代码即可,意思是根据配置文件中的配置调用初始化脚本。runSchema(dataSource, value, dbName)的代码如下:
private static void runSchema(DataSource datasource, String schemas, String dbName) {
InputStream is = null;
try (Connection conn = datasource.getConnection()) {
ScriptRunner scriptRunner = new ScriptRunner(conn);
scriptRunner.setStopOnError(false);
for (String schemaPath : schemas.split(StrConstantEnum.COMMA.getValue())) {
schemaPath = schemaPath.trim();
if (schemaPath.startsWith(CommonConstant.CLASSPATH)) {
ClassPathResource classPathResource = new ClassPathResource(StringUtils.substringAfter(schemaPath, CommonConstant.CLASSPATH));
is = classPathResource.getInputStream();
} else {
is = new FileInputStream(schemaPath);
}
try (InputStreamReader isr = new InputStreamReader(is)) {
scriptRunner.runScript(isr);
}
}
} catch (SQLException | IOException e) {
log.warn(MessageFormat.format("数据源:{0}运行初始化脚本失败", dbName), e);
}
}
主要使用了Mybatis的执行脚本功能,在初始化datasource后获取一个connection,读取某个位置存储的一段SQL脚本并执行。这样在不使用DataSourceAutoConfiguration或其他datasource自动配置方式的情况下,也能实现类似schema脚本的功能啦。