前面介绍的两篇基于配置方式的数据库初始化方式,使用起来非常简单,但是有一个非常明显的问题,如何实现表结构存在时不再初始化,不存在时才执行? 如果数据库也不存在,也需要初始化时创建,可行么?
接下来介绍一下如何使用DataSourceInitializer来实现自主可控的数据初始化
I. 项目搭建
1. 依赖
首先搭建一个标准的SpringBoot项目工程,相关版本以及依赖如下
本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发
1 2 3 4 5 6 7 8 9 10
| <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> </dependencies>
|
本文使用MySql数据库, 版本8.0.31
2. 配置
注意实现初始化数据库表操作的核心配置就在下面,重点关注
配置文件: resources/application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| database: name: story
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/${database.name}?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password:
logging: level: root: info org: springframework: jdbc: core: debug
|
注意上面的配置,我们新定义了一个数据库的配置项 database.name, 主要是为了检测database是否存在,若不存在时,创建对应的数据库时使用
接下来是初始化sql脚本
resources/init-schema.sql 对应的初始化ddl
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| CREATE TABLE `user` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `third_account_id` varchar(128) NOT NULL DEFAULT '' COMMENT '第三方用户ID', `user_name` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名', `password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码', `login_type` tinyint NOT NULL DEFAULT '0' COMMENT '登录方式: 0-微信登录,1-账号密码登录', `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间', PRIMARY KEY (`id`), KEY `key_third_account_id` (`third_account_id`), KEY `user_name` (`user_name`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户登录表';
|
resources/init-data.sql 对用的初始化dml
1 2
| INSERT INTO `user` (id, third_account_id, `user_name`, `password`, login_type, deleted) VALUES (1, 'a7cb7228-0f85-4dd5-845c-7c5df3746e92', 'admin', 'admin', 0, 0);
|
II. 初始化
1.初始化配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Slf4j @Configuration public class DataSourceInit {
@Value("classpath:init-schema.sql") private Resource schemaSql;
@Value("classpath:init-data.sql") private Resource initData;
@Value("${database.name}") private String database;
@Bean public DataSourceInitializer dataSourceInitializer(final DataSource dataSource) { final DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); initializer.setDatabasePopulator(databasePopulator()); initializer.setEnabled(needInit(dataSource)); return initializer; }
private DatabasePopulator databasePopulator() { final ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); populator.addScripts(schemaSql); populator.addScripts(initData); populator.setSeparator(";"); return populator; }
}
|
我们这里主要是借助 DataSourceInitializer 来实现初始化,其核心有两个配置
- DatabasePopulator: 通过
addScripts来指定对应的sql文件
- DataSourceInitializer#setEnabled: 判断是否需要执行初始化
接下来重点需要看的就是needInit方法,我们再这个方法里面,需要判断数据库是否存在,若不存在时,则创建数据库;然后再判断表是否存在,以此来决定是否需要执行初始化方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
private boolean needInit(DataSource dataSource) { if (autoInitDatabase()) { return true; } JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); List list = jdbcTemplate.queryForList("SELECT table_name FROM information_schema.TABLES where table_name = 'user' and table_schema = '" + database + "';"); boolean init = CollectionUtils.isEmpty(list); if (init) { log.info("库表不存在,执行建表及数据初始化"); } else { log.info("表结构已存在,无需初始化"); } return init; }
private boolean autoInitDatabase() { URI url = URI.create(SpringUtil.getConfig("spring.datasource.url").substring(5)); String uname = SpringUtil.getConfig("spring.datasource.username"); String pwd = SpringUtil.getConfig("spring.datasource.password"); try (Connection connection = DriverManager.getConnection("jdbc:mysql://" + url.getHost() + ":" + url.getPort() + "?useUnicode=true&characterEncoding=UTF-8&useSSL=false", uname, pwd); Statement statement = connection.createStatement()) { ResultSet set = statement.executeQuery("select schema_name from information_schema.schemata where schema_name = '" + database + "'"); if (!set.next()) { String createDb = "CREATE DATABASE IF NOT EXISTS " + database; connection.setAutoCommit(false); statement.execute(createDb); connection.commit(); log.info("创建数据库({})成功", database); if (set.isClosed()) { set.close(); } return true; } set.close(); log.info("数据库已存在,无需初始化"); return false; } catch (SQLException e2) { throw new RuntimeException(e2); } }
|
上面的数据库判断是否存在以及初始化的过程相对基础,直接使用了基础的Connection进行操作;这里借助了SpringUtil来获取配置信息,对应的类源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| package com.git.hui.schema.config;
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component;
@Component public class SpringUtil implements ApplicationContextAware, EnvironmentAware { private static ApplicationContext context; private static Environment environment;
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringUtil.context = applicationContext; }
@Override public void setEnvironment(Environment environment) { SpringUtil.environment = environment; }
public static <T> T getBean(Class<T> bean) { return context.getBean(bean); }
public static Object getBean(String beanName) { return context.getBean(beanName); }
public static String getConfig(String key) { return environment.getProperty(key); }
public static void publishEvent(ApplicationEvent event) { context.publishEvent(event); } }
|
到此整个初始化相关的配置已经完成;接下来我们验证一下
2.验证
再项目启动成功之后,查看一下数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Slf4j @SpringBootApplication public class Application implements ApplicationRunner { @Autowired private JdbcTemplate jdbcTemplate;
public static void main(String[] args) { SpringApplication.run(Application.class); }
@Override public void run(ApplicationArguments args) throws Exception { List list = jdbcTemplate.queryForList("select * from user limit 2"); log.info("启动成功,初始化数据: {}\n{}", list.size(), list); } }
|

3. 小结
本文主要介绍的是基于DataSourceInitializer来实现自主可控的数据初始化,其核心配置为
- DatabasePopulator: 通过
addScripts来指定对应的sql文件
- DataSourceInitializer#setEnabled: 判断是否需要执行初始化
此外本文还介绍了如何判断数据库是否存在,当数据库不存在时,借助基础的Connection来建立连接,创建数据库;从初始化角度来看,这几篇文中介绍的方式已经足够,但是在项目制的场景下,我们需要记录数据库的版本迭代记录,下一篇将介绍如何使用liquibase来实现数据版本管理,解决初始化以及增量的迭代变更
III. 不能错过的源码和相关知识点
0. 项目
1. 微信公众号: 一灰灰Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

打赏
如果觉得我的文章对您有帮助,请随意打赏。
微信打赏
支付宝打赏