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

不需要在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.xml
    enabled: true # 当实际使用的数据库不支持liquibase,如 mariadb 时,将这个参数设置为false

说明:

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

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

<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
        xmlns="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">

    <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"?>
<databaseChangeLog
        xmlns="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方式-数据库初始化方式

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java毕业设计 基于SSM SpringBoot vue购物比价网站

Java毕业设计 基于SSM SpringBoot vue购物比价网站 SSM vue 购物比价网站 功能介绍 首页 图片轮播 商品 商品分类 商品详情 评论 收藏 商品攻略 商品信息 公告栏 在线反馈 登录 注册 个人中心 我的收藏 后台管理 登录 注册商品户 个人中心 修改密码 个人信息 商品户管理 用户…

污水处理设备远程监控:HiWoo Cloud如何打造智慧环保新时代

随着工业化和城市化的快速推进&#xff0c;污水处理成为了保护环境、维护生态平衡的重要一环。传统的污水处理设备管理方式往往依赖于人工巡检和现场控制&#xff0c;不仅效率低下&#xff0c;而且难以实时掌握设备的运行状况。在这个背景下&#xff0c;HiWoo Cloud平台推出了污…

如何使用固定公网地址远程访问本地RStudio Server【内网穿透】

文章目录 前言1. 安装RStudio Server2. 本地访问3. Linux 安装cpolar4. 配置RStudio server公网访问地址5. 公网远程访问RStudio6. 固定RStudio公网地址 前言 RStudio Server 使你能够在 Linux 服务器上运行你所熟悉和喜爱的 RStudio IDE&#xff0c;并通过 Web 浏览器进行访问…

【小沐学C++】C/C++包管理工具Conan使用(C++、Python、CMake、Conan)

文章目录 1、简介2、Conan下载安装3、Conan命令3.1 查看conan版本3.2 更新conan库3.3 搜索软件包3.4 显示conan所有源3.5 查看软件包3.6 通过conanfile.txt安装依赖包 结语 1、简介 Conan是C和C语言的依赖项和包管理器。它是免费和开源的&#xff0c;适用于所有平台&#xff0…

知识蒸馏 pytorch官网源码分析

参考连接&#xff1a; Knowledge Distillation Tutorial — PyTorch Tutorials 2.2.1cu121 documentation 方法一 &#xff1a; 知识蒸馏的损失函数只接受两个相同维度的输入&#xff0c;所以我们需要采取措施使他们在进入损失函数之前是相同维度的。我们将使用平均池化…

考研初试没过线怎么办?你还有这些机会!

2024年研考初试成绩已经发布。 研究生考试国家线大概率在下个月公布。 过了国家线的同学可以继续准备复试或者选择调剂。 如果初试失利没过线&#xff0c;也不要急着放弃&#xff01; 我们还有上岸拿硕士学位的机会&#xff01; 考研初试没过线,我们还可以这样入学&#x…

【JavaEE进阶】 Spring AOP详解

文章目录 &#x1f38b;前言&#x1f38d;Spring AOP核心概念&#x1f6a9;切点(Pointcut)&#x1f6a9;连接点(Join Point)&#x1f6a9;通知(Advice)&#x1f6a9;切面(Aspect) &#x1f340;通知类型&#x1f6a9;注意事项 &#x1f332;PointCut&#x1f384;切面优先级Ord…

【JavaEE进阶】 Spring AOP快速上手

文章目录 &#x1f343;什么是AOP&#x1f333;什么是Spring AOP&#x1f334;上手Spring AOP&#x1f6a9;引入依赖&#x1f6a9;编写AOP程序 ⭕总结 &#x1f343;什么是AOP AOP是Aspect Oriented Programming的简称&#xff08;又称为面向切⾯编程&#xff09; 什么是面向…

面试笔记系列四之SpringBoot+SpringCloud+计算机网络基础知识点整理及常见面试题

目录 Spring Boot 什么是 Spring Boot&#xff1f; Spring Boot 有哪些优点&#xff1f; SpringBootApplication注解 Spring Boot 的启动流程 Spring Boot属性加载顺序 springboot自动配置原理是什么&#xff1f;&#xff08;*&#xff09; 如何理解springboot中的start…

ISP代理是什么?跨境账号养号为什么要选择它?

在跨境出海业务中&#xff0c;代理IP对于您的在线任务至关重要&#xff0c;尤其是对于那些运行多个帐户的人来说。为您的帐户选择正确类型的代理对于确保帐户安全非常重要&#xff0c;劣质的IP容易使账号遭受封号风险。IPFoxy的多种代理IP类型应用范围各有侧重&#xff0c;其中…

ICML 2023 | 可证明的动态多模态融合框架论文整理

主要思想 提出一种针对低质量数据的通用的动态多模态融合框架&#xff0c;利用多模态融合得到的泛化误差去动态更新各个单模态预测器, 使得多模态决策倾向于更多地依赖于高质量模态 &#xff0c;而不是其他模态 。通过动态确定每种模态的融合权重来减轻不可靠模态的影响。&…

3月5日济南,2024生物发酵展全新起航!助力打造生物产业经济新时代

生物发酵是生物产业的重要组成部分&#xff0c;近年来&#xff0c;我国生物产业发展迅猛&#xff0c;生物发酵技术也已广泛应用于食品、农业、医药、饲料、日化、材料等领域&#xff0c;市场前景广阔。2022年5月&#xff0c;国家发改委发布的《“十四五”生物经济发展规划》明确…

霍格沃兹遗产显示找不到emp.dll无法继续执行此代码的多种解决方法

在《霍格沃茨&#xff1a;遗产》这款游戏中&#xff0c;如果发现无法找到必要的文件emp.dll&#xff0c;emp.dll作为游戏运行过程中不可或缺的核心组件之一&#xff0c;它的缺失会导致游戏根本无法启动&#xff0c;玩家将无法正常进入并探索这个充满魔法与奇幻色彩的霍格沃茨世…

MySQL(基础篇)——函数、约束

一.函数 1.定义 函数是指一段可以直接被另一段程序调用的程序或代码。 2.字符串函数 常见如下&#xff1a; -- 字符串拼接 SELECT CONCAT(hello,MySql) AS CONCAT -- 将字符串全部转为小写 SELECT LOWER(HEllo MYSql) AS LOWER -- 将字符串全部转为大写 SELECT UPPER(Hello…

(C语言)二分查找

在⼀个升序的数组中查找指定的数字n&#xff0c;很容易想到的⽅法就是遍历数组&#xff0c;但是这种⽅法效率⽐较低。⽐如我买了⼀双鞋&#xff0c;你好奇问我多少钱&#xff0c;我说不超过300元。你还是好奇&#xff0c;你想知道到底多少&#xff0c;我就让你猜&#xff0c;你…

谷歌SEO推广提高网站点击率的10个秘籍-华媒舍

在当今数字化时代&#xff0c;拥有一个高点击率的网站对于企业和个人而言至关重要。通过谷歌SEO推广&#xff0c;可以帮助网站吸引更多的流量&#xff0c;并在搜索引擎结果页面&#xff08;SERP&#xff09;中获得更好的排名。本文将介绍10个谷歌SEO推广的秘籍&#xff0c;帮助…

ubuntu新建ap热点并分享

测试环境ubuntu16,只有一台笔记本电脑&#xff0c;不插网线&#xff0c;无线网卡既连wifi&#xff0c;又作为热点 1.方法1 直接手动新建ap热点 参考https://jingyan.baidu.com/article/ea24bc39b03fc6da62b331f0.html https://jingyan.baidu.com/article/363872ecd8f35d6e4ba…

基于React, Redux实现的俄罗斯方块游戏及源码

分享一个俄罗斯方块游戏游戏框架使用的是 React Redux&#xff0c;其中再加入了 Immutable&#xff0c;用它的实例来做来Redux的state。&#xff08;有关React和Redux的介绍可以看 安装 npm install运行 npm start浏览自动打开 http://127.0.0.1:8080/ 打包编译 npm run …

码住!2024抖音电商爆品攻略,21个行业68个类目一文集齐

小商家&#xff0c;大生意。 2023年&#xff0c;抖音电商高歌猛进&#xff0c;用直播带货创造增量&#xff0c;以商品卡承接用户资产&#xff0c;跑出2万亿市场&#xff0c;带动万千品牌实现二次增长&#xff0c;无数中小商家随奔腾浪潮涌出&#xff0c;从无名到争先&#xff…

【element-ui】el-select multiple多选,表单校验问题解决方法

在项目开发过程中发现&#xff0c;el-select设置了multiple支持多选属性之后&#xff0c;el-select赋值之后&#xff0c;表单校验不通过 解决思路及解决方法&#xff1a; 1、首先看看v-model 、prop属性、rules校验是否正确&#xff0c;这里注意el-select的rules校验的trigger…