技术派数据库表自动初始化(学习)

不需要在db中手动创建或者导入相关的schema、data,项目启动自动创建对应的表,并初始化。实现该过程。

Liquibase数据库版本管理

依赖配置

在paicoding-web模块中,pom.xml 文件中添加

        <dependency><groupId>org.liquibase</groupId><artifactId>liquibase-core</artifactId></dependency>

然后就是核心配置,首先是application.yml配置文件中,有两个关键参数

spring:liquibase:change-log: classpath:liquibase/master.xmlenabled: true # 当实际使用的数据库不支持liquibase,如 mariadb 时,将这个参数设置为false

说明:

  • 对于使用的数据库不支持liquibase,如 mariadb 时,将这个参数设置为false 
  • change-log:对应的是核心的数据库版本变更配置

master.xml文件中的内容如下:

<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLogxmlns="http://www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangeloghttp://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"><include file="liquibase/changelog/000_initial_schema.xml" relativeToChangelogFile="false"/></databaseChangeLog>

注意上面这个include,这个就是告诉liqubase,所有的变更记录,都放在

liquibase/changelog/000_initial_schema.xml这个文件中

现在只有一个include标签,但是实际上是可以有多个的,一个好的建议是,项目首次初始化表、初始化数据可以是一个include标签;后续每次大的版本迭代,对应一个新的include。

再看一下000_initial_schema.xml里面的文件内容

<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLogxmlns="http://www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"><property name="now" value="now()" dbms="mysql"/><property name="autoIncrement" value="true"/><changeSet id="00000000000001" author="YiHui"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/init_schema_221209.sql"/></changeSet><changeSet id="00000000000002" author="YiHui"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/init_data_221209.sql"/></changeSet><changeSet id="00000000000003" author="YiHui"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/init_data_221210.sql"/></changeSet><changeSet id="00000000000005" author="YiHui"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/init_data_221216.sql"/></changeSet><!--  专栏类型,新增免费开始时间、结束时间  --><changeSet id="00000000000006" author="YiHui"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/update_schema_221223.sql"/></changeSet><!-- user_info表添加ip字段,用于记录访问用户所在地   --><changeSet id="00000000000007" author="YiHui"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/update_schema_221229.sql"/></changeSet><!-- 配置表新增 extra 字段 --><changeSet id="00000000000008" author="YiHui"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/update_schema_230103.sql"/></changeSet><!-- 技术派介绍文章 --><changeSet id="00000000000009" author="YiHui"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/init_data_230103.sql"/></changeSet><!-- 重新更新标签 --><changeSet id="00000000000012" author="LouZai"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/init_data_230105.sql"/></changeSet><!-- 添加审核中 --><changeSet id="00000000000013" author="LouZai"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/init_data_230130.sql"/></changeSet><!-- 添加用户角色 --><changeSet id="00000000000014" author="YiHui"><sqlFile dbms="mysql" endDelimiter=";" encoding="UTF-8" path="liquibase/data/update_schema_230131.sql"/></changeSet>
</databaseChangeLog>

说明:

  • changeSet标签,id必须唯一,不能出现冲突
  • sqlFile里面的path,对应的可以是标准的sql文件,也可以是xml格式的数据库表定义、数据库操作文件
  • 一旦写上去,changeSet的顺徐不要调整

比如库表创建的sql

项目演示

在技术派的项目中,还做了一个事情,就是初始化库,在下面的DataSourceInitializer中有介绍;如果是一个新的项目,接入Liquibase之后,数据库,请注意还是需要自己来创建的

项目启动之后,一切正常的话,直接连接上数据库可以看到库表创建成功,数据也初始化完成,当然也可以是直接观察控制台的输出。

下面红框中的ChangeSet xxx run successfully  in 401ms 就表示对应的sql执行成功了

注意事项

非常重要的一个点是,上面的每个ChangeSet只会执行一次,因此当执行完毕之后发现不对,要回滚怎么办,或者需要修改怎么办?需要Liquibase提供的回滚机制。简单说明。

当Change执行完毕之后,对应的sql文件/xml文件(即path定义的文件)不允许在修改,因为db中会记录这个文件的md5,当修改这个文件之后,这个MD5也会随之发生改变

有两个方案解决:新增一个changeSte

删除DATABASECHANFELOG表中changeSet id对应的记录,然后重新走一遍

DataSourceInitializer首次初始化方案

我们这里主要借助DataSourceInitializer来实现初始化,其核心有两个配置

  • DatabasePopulator:通过addCcripts来指定对应的的sql文件
  • DataSourceInitializer#setEnable;判断是否需要执行初始化

我们主要借助DataSourceInitializer来实现Liquibase的表的创建、数据变更等操作;但是在此之前,我们还做了一个库的初始化

库初始化

接下来重点要看的就是needInit方法,我们在这个方法里面,需要判断数据库是否存在,若不存在时,则创建数据库;然后判断表是否存在,以此来决定是否需要执行初始化方法。

入口在实现ForumDataSourceInitializer

  /*** 检测一下数据库中表是否存在,若存在则不初始化;否则基于 schema-all.sql 进行初始化表** @param dataSource* @return*/private boolean needInit(DataSource dataSource) {if (autoInitDatabase()) {return true;}// 根据是否存在表来判断是否需要执行sql操作JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);List list = jdbcTemplate.queryForList("SELECT table_name FROM information_schema.TABLES where table_name = 'user_info' and table_schema = '" + database + "';");return CollectionUtils.isEmpty(list);}/*** 数据库不存在时,尝试创建数据库*/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);}}

上面的实现比较清晰了,首先是判断数据库是否存在,这里需要注意的就是,我们需要自己额创建db的连接,并执行相关库的判断、初始化sql执行。

为什么不直接使用spring.datasource.url来创建连接?

        因为库不存在时,直接使用下面这个url进行连接会抛出异常

表初始化

表初始化,其实可以理解为项目启动后要执行的一些sql,这时主要借助就是initializer.setDatabasePopulator

核心知识点

虽然技术派新增了一个DbChangeSetLoader类来实现初始化sql的加载,但实际上,若你完全抛开Liquibase,单纯的希望项目启动后执行某些sql,可以非常简单的实现,直接用下面这种就可以了啦。

  • 通过@Value来加载需要初始化的sql文件
  • 直接通过ResourceDatabasePoplulator添加sql资源

Liquibase兼容方案

在技术派中,做了Liquibase的兼容,即找那些sql需要进行初始化,完全在遵循了Liquibase中定义的xml文件

要想要在技术派中使用这种方式进行初始化,如使用marizadb时,需要修改配置参数

spring.liquibase.enable:false

核心的实现如下:

对于liquibase的xml文件解析,核心逻辑在DbChangeSetLoader中,借助sax来进行xml文件的解析(Spring也是用sax解析xml的)

实现看源码,有两个知识点:

1.如何加载xml文件

        下面的传参set是相对路径,如liquibase/data/init_data_221216.sql

2、sax的xml解析

小结

介绍项目启动之后库表的初始化操作,结合实际的代码介绍了两种使用姿势

  • Liquibase:代表的数据库版本管理方式
  • DataSourceInitializer:代表的项目启动之后执行某些初始化方式

更多关注:

DataSourceInitializer方式-数据库初始化方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/265480.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rocky Linux 运维工具 mv

一、mv的简介 ​​mv​是Linux系统中的命令&#xff0c;用于移动文件或重命名文件。它可以在同一文件系统内将文件从一个目录移动到另一个目录&#xff0c;也可以修改文件的名称。 二、mv的参数说明 1、 三、mv的实战示例 1、重命名 ###查看目录/root/下的文件列表 [rootloc…

Java中使用Jsoup实现网页内容爬取与Html内容解析并使用EasyExcel实现导出为Excel文件

场景 Pythont通过request以及BeautifulSoup爬取几千条情话&#xff1a; Pythont通过request以及BeautifulSoup爬取几千条情话_爬取情话-CSDN博客 Node-RED中使用html节点爬取HTML网页资料之爬取Node-RED的最新版本&#xff1a; Node-RED中使用html节点爬取HTML网页资料之爬…

Java JVM虚拟机面试题

Java JVM虚拟机面试题 前言1、ThreadLocal的底层原理和应用&#xff1f;2、Java中的锁池和等待池&#xff1f;3、wait()&#xff0c;yield()&#xff0c;join()&#xff0c;sleep()的区别&#xff1f;4、你们项⽬如何排查JVM问题&#xff1f;5、YGC和FGC发生时间&#xff1f;6、…

vue.config.js publicPath 和 vue-router base 结合配置项目根目录为二级目录案例

背景: 同个域名下需要有 PC 管理后台, H5 端, 企业微信 ......等多个端, 需要在一个域名下通过不同的路径来区分不同的项目; 例如: abc.com/pc, abc.com/h5, abc.com/wx-work.... 此处做个记录 步骤: 1. 修改 vue.config.js 中的 publicPath module.exports {outputDir:…

React18源码: Fiber树中的全局状态与双缓冲

Fiber树构造 在React运行时中&#xff0c;fiber树构造位于 react-reconciler 包在正式解读 fiber 树构造之前&#xff0c;再次回顾一下renconciler的4个阶段 1.输入阶段&#xff1a;衔接react-dom包&#xff0c;承接fiber更新请求2.注册调度任务&#xff1a;与调度中心(schedu…

(二十三)Flask之高频面试点

目录&#xff1a; 每篇前言&#xff1a;Q1&#xff1a;为什么把request和session放在一起&#xff1f;Q2&#xff1a;Local对象的作用&#xff1f;Q3:&#xff1a;LocalStack对象的作用&#xff1f;Q4&#xff1a;一个运行中的Flask应用程序分别包括几个Local/LocalStack&#…

Spring11、整合Mybatis

11、整合Mybatis 步骤&#xff1a; 导入相关jar包 junit <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version> </dependency> mybatis <dependency><groupId>org.my…

docker小知识:linux环境安装docker

安装必要软件包&#xff0c;执行如下命令 yum install -y yum-utils device-mapper-persistent-data lvm2目的是确保在安装 Docker 之前&#xff0c;系统已经安装了必要的软件包和服务&#xff0c;以支持 Docker 的正常运行。设置yum源&#xff0c;添加Docker官方的CentOS存储…

sonar-java 手写一个规则-单元测试分析

前言 最近做项目&#xff0c;定制sonar规则&#xff0c;提高Java代码质量&#xff0c;在编写的sonar规则&#xff0c;做验证时&#xff0c;使用单元测试有一些简单的心得感悟&#xff0c;分享出来。 自定义规则模式 sonar的自定义规则很简单&#xff0c;一般而言有2种模式可…

udp服务器【Linux网络编程】

目录 一、UDP服务器 1、创建套接字 2、绑定套接字 3、运行 1&#xff09;读取数据 2&#xff09;发送数据 二、UDP客户端 创建套接字&#xff1a; 客户端不用手动bind 收发数据 处理消息和网络通信解耦 三、应用场景 1、服务端执行命令 2、Windows上的客户端 3…

【亚马逊云新春特辑②】构生成式 AI 文生图工具之借助ControlNet进行AI绘画创作【生成艺术二维码】

文章目录 1.1 生成艺术二维码1&#xff09;制作基础二维码2&#xff09;确定艺术风格3&#xff09;生成艺术二维码4&#xff09;结果优化 AIGC 的可控性是它进入实际生产最关键的一环。在此之前&#xff0c;许多用户希望 AI 生成的结果尽可能符合要求&#xff0c;但都不尽如人意…

linux服务器调度数据库的存储过程

1、需要安装数据库的客户端 2、安装sqlplus 3、编写sh脚本 脚本内容如下&#xff1a; 4、设置调度任务

【GPTs分享】每日GPTs分享之Image Generator Tool

今日GPTs分享&#xff1a;Image Generator Tool。Image Generator Tool是一种基于人工智能的创意辅助工具&#xff0c;专门设计用于根据文字描述生成图像。这款工具结合了专业性与友好性&#xff0c;鼓励用户发挥创造力&#xff0c;同时提供高效且富有成效的交互体验。 主要功能…

4. client-go 编程式交互

Kubernetes 系统使用 client-go 作为 Go 语言的官方编程式交互客户端库&#xff0c;提供对 Kubernetes API Server 服务的交互访问。Kubernetes 的源码中已经集成了 client-go 的源码&#xff0c;无须单独下载。client-go 源码路径为 vendor/k8s.io/client-go。 开发者经常使用…

rviz显示双臂ur10

注意有线网的连接 注意这里rviz只做显示用&#xff0c;并没有结合moveit 步骤总结如下&#xff1a; launch文件&#xff1a;这里tf加上域名&#xff0c;是tool0_controller和base两个tf的前缀 在luanch文件最后就统一加载一次模型&#xff0c;传递两个参数 这里加上另一个机…

【Go语言】Go语言中的切片

Go语言中的切片 1.切片的定义 Go语言中&#xff0c;切片是一个新的数据类型数据类型&#xff0c;与数组最大的区别在于&#xff0c;切片的类型中只有数据元素的类型&#xff0c;而没有长度&#xff1a; var slice []string []string{"a", "b", "c…

大模型(LLM)的token学习记录-I

文章目录 基本概念什么是token?如何理解token的长度&#xff1f;使用openai tokenizer 观察token的相关信息open ai的模型 token的特点token如何映射到数值&#xff1f;token级操作&#xff1a;精确地操作文本token 设计的局限性 tokenizationtoken 数量对LLM 的影响训练模型参…

【React源码 - 调度任务循环EventLoop】

我们知道在React中有4个核心包、2个关键循环。而React正是在这4个核心包中运行&#xff0c;从输入到输出渲染到web端&#xff0c;主要流程可简单分为一下4步&#xff1a;如下图&#xff0c;本文主要是介绍两大循环中的任务调度循环。 4个核心包&#xff1a; react&#xff1a;…

day02_前后端环境搭建(前端工程搭建,登录功能说明,后端项目搭建)

文章目录 1. 软件开发介绍1.1 软件开发流程1.2 角色分工1.3 软件环境1.4 系统的分类 2. 尚品甄选项目介绍2.1 电商基本概念2.1.1 电商简介2.1.2 电商模式B2BB2CB2B2CC2BC2CO2O 2.2 业务功能介绍2.3 系统架构介绍2.4 前后端分离开发 3. 前端工程搭建3.1 Element-Admin简介3.2 El…

WordPress前端如何使用跟后台一样的Dashicons图标字体?

很多站长都喜欢在站点菜单或其他地方添加一些图标字体&#xff0c;常用的就是添加Font Awesome 图标和阿里巴巴矢量库图标iconfont。其实我们使用的 WordPress 本身就有一套管理员使用的官方图标字体 Dashicons&#xff0c;登录我们站点后台就能看到这些图标字体。那么有没有可…