liquibase入门

Liquibase定义

Liquibase是一个用于跟踪、管理和应用数据库变化的开源的数据库重构工具。它将所有数据库的变化(包括结构和数据)都保存在XML文件中,便于版本控制。
简单来说,liquibase就是数据库的git,用于管理数据库的所有变动

Liquibase功能

  • 不依赖于特定的数据库,目前支持包括Oracle/Sql
    Server/DB2/MySql/Sybase/PostgreSQL/Caché等12种数据库,这样在数据库的部署和升级环节可帮助应用系统支持多数据库。

  • 提供数据库比较功能,比较结果保存在XML中,基于该XML你可用Liquibase轻松部署或升级数据库。

  • 以XML存储数据库变化,其中以作者和ID唯一标识一个变化(ChangSet),支持数据库变化的合并,因此支持多开发人员同时工作。

  • 在数据库中保存数据库修改历史(DatabaseChangeHistory),在数据库升级时自动跳过已应用的变化(ChangSet)。

  • 提供变化应用的回滚功能,可按时间、数量或标签(tag)回滚已应用的变化。通过这种方式,开发人员可轻易的还原数据库在任何时间点的状态。

  • 可生成数据库修改文档(HTML格式) 提供数据重构的独立的IDE和Eclipse插件。

Liquibase好处

减少手动管理数据库的效率低下和出错率高的问题,便于数据库版本的更替
它将所有数据库的变化(包括结构和数据)都保存在XML文件中,便于版本控制。

简单示例

一 创建一个spring boot项目
二 引入核心依赖
<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
    <version>4.0.5</version>
</dependency>
三 引入数据库依赖

驱动版本和数据库版本要匹配

      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.47</version>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-jpa</artifactId>
          <exclusions>
              <exclusion>
                  <groupId>org.hibernate</groupId>
                  <artifactId>hibernate-core</artifactId>
              </exclusion>
          </exclusions>
      </dependency>
四 在application.yml写入数据库连接配置
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/liquibasedemo?useSSL=true&useUnicode=true&characterEncoding=utf8
    username: root
    password:
  liquibase:
#    是否开启liquibase(默认为true)
    enabled: true
#    配置文件的路径(默认为classpath:/db/changelog/db.changelog-master.yaml)
    change-log: classpath:/db/changelog/changelog-master.xml
#    是否先 drop schema(默认为false)
    drop-first: fals
五 添加liquibase的配置类 config/LiquiBaseConfig.java
import liquibase.integration.spring.SpringLiquibase;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@Configuration
public class LiquibaseConfig {
  @Bean
  public SpringLiquibase springLiquibase(DataSource dataSource){
      SpringLiquibase springLiquibase = new SpringLiquibase();
      springLiquibase.setDataSource(dataSource);
      springLiquibase.setChangeLog("classpath:liquibase/liquibase-master.xml");
      springLiquibase.setDropFirst(false);
      springLiquibase.setShouldRun(true);
      return springLiquibase;
  }
}
六 在资源目录(resources)下创建liquibase目录,在liquibase目录下创建liquibase-master.xml文件
使用xml文件编写sql
<?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.4.xsd">
    <include file="classpath:liquibase/change_log/2020-02-06-init-schema.xml" relativeToChangelogFile="false"/>
</databaseChangeLog>

使用sql文件编写sql

<?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.1.xsd">

  <!-- 
  1.file:表示将此路径下 /sql/001_create_person_table.sql 目录中的 changelog 文件引入。
  假设 db 目录下还有其他模块(目录),继续通过 <include> 或 <includeAll> 元素引入即可。 
  2.relativeToChangelogFile:为 true 时表示使用的是相对路径 而不是classpath
  -->
  <include file="/sql/001_create_person_table.sql" relativeToChangelogFile="true"></include>
	
  <changeSet id="T100-20221009-001" author="txg">
      <sqlFile path="/db/changelog/sql/002_create_person1_table.sql"></sqlFile>
  </changeSet>
  
</databaseChangeLog>

七 在资源目录下,创建liquibase目录,liquibase目录下创建change_log目录,change_log目录下创建2020-02-06-init-schema.xml文件,内容如下

(changeSet id属性唯一 同一个id只会被执行一次)

<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.4.xsd">
    <property name="autoIncrement" value="true" dbms="mysql"/>
    <changeSet id="init-schema" author="Young" >
        <comment>init schema</comment>
        <createTable tableName="user">
            <column name="id" type="bigint" autoIncrement="${autoIncrement}">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="nick_name" type="varchar(255)">
                <constraints  nullable="false"/>
            </column>
            <column name="email" type="varchar(255)">
                <constraints  nullable="false"/>
            </column>
            <column name="register_time" type="timestamp"  defaultValueComputed="CURRENT_TIMESTAMP">
                <constraints nullable="false"/>
            </column>
        </createTable>
    </changeSet>
</databaseChangeLog>
在 src/main/resources/db 创建目录 changelog/sql,用来存放数据库脚本文件;

例如,创建 001_create_person_table.sql 文件

  • 每个sql文件必须以-- liquibase formatted sql注释开头
  • 每个changeset必须以–changeset author:id注释开头
-- liquibase formatted sql

-- changeset txg:2023_03_05
insert into departments values(1, '研发部');
insert into departments values(2, '销售部');
-- changeset ttt:2023_03_06
insert into departments values(3, '后勤部');


-- changeset ggg:2023_03_06
DELETE FROM departments WHERE DEPARTMENT_NAME = '研发部'



八 启动项目即可完成sql执行 新增表和两张系统表

databasechangelog和databasechangeloglock表

LiquiBase在执行changelog时,会在数据库中插入两张表:DATABASECHANGELOG和DATABASECHANGELOGLOCK,分别记录changelog的执行日志和锁日志。

LiquiBase在执行changelog中的changeSet时,会首先查看DATABASECHANGELOG表,若是已经执行过,则会跳过(除非changeSet的runAlways属性为true),若是没有执行过,则执行并记录changelog日志;

changelog中的一个changeSet对应一个事务,在changeSet执行完后commit,若是出现错误则rollback;

标签的主要属性有:dom

  • runAlways:即便已经执行过,仍然每次都执行;注意:
    因为DATABASECHANGELOG表中还记录了changeSet的MD5校验值MD5SUM,若是changeSet的id和name没变,而内容变了,则因为MD5值变了,即便runAlways的值为True,执行也是失败的,会报错。这种状况应该使用runOnChange属性。
  • runOnChange:第一次的时候执行以及当changeSet的内容发生变化时执行。不受MD5校验值的约束。
  • runInTransaction:是否做为一个事务执行,默认为true。设置为false时须要当心:若是执行过程当中出错了则不会rollback,数据库极可能处于不一致的状态;
<changeSet author="cavan" id="22.1.V1-1">
        <preConditions onFail="MARK_RAN">
            <not>
                <tableExists tableName="school"/>
            </not>
        </preConditions>
        <createTable tableName="school">
            <!-- int类型 -->
            <column name="school_id" type="INT" autoIncrement="true">
                <constraints primaryKey="true" nullable="false"/>
            </column>

            <!-- 字符串类型 -->
            <column name="school_name" type="VARCHAR(100)">
                <constraints nullable="true" unique="false"/>
            </column>

            <!-- 文本类型 -->
            <column name="address" type="TEXT"/>

            <!-- boolean类型 使用boolean或者tinyint(1)-->
            <column name="is_top_ten" type="boolean" defaultValueBoolean="true"/>
            <!-- 方式二
                <column defaultValueNumeric="0" name="is_top_ten" type="tinyint(1)"/>
                -->

            <column defaultValue="anonymity" name="created_by" type="VARCHAR(50)"/>
            <column defaultValueComputed="CURRENT_TIMESTAMP" name="created_date" type="TIMESTAMP">
                <constraints nullable="false"/>
            </column>
            <column defaultValueComputed="CURRENT_TIMESTAMP" name="last_modified_date" type="TIMESTAMP">
                <constraints nullable="false"/>
            </column>
            <column defaultValue="anonymity" name="last_modified_by" type="VARCHAR(50)"/>
        </createTable>	
    </changeSet>

    <changeSet author="cavan" id="22.1.V1-2">
        <preConditions onFail="MARK_RAN">
            <not>
                <tableExists tableName="school_class"/>
            </not>
        </preConditions>
        <createTable tableName="school_class">
            <column name="class_id" type="INT">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="class_name" type="VARCHAR(100)">
                <constraints nullable="true"/>
            </column>
            <column name="school_id" type="INT">
                <constraints nullable="false"/>
            </column>
        </createTable>	
    </changeSet>

    <!-- 删除表 -->
    <changeSet author="cavan" id="22.1.V1-3">
        <preConditions>
            <tableExists tableName="school_class" />
        </preConditions>
        <dropTable tableName="school_class" />
    </changeSet>

    <!-- 修改表名 -->
    <changeSet author="cavan" id="22.1.V1-4">
        <preConditions>
            <tableExists tableName="school_class" />
        </preConditions>

        <renameTable oldTableName="school_class" 
                     newTableName="class_school"/> 
    </changeSet>


    <!-- ################### 其他相关表操作 ################### -->
    <!-- 增加主键,单一主键 -->
    <changeSet author="cavan" id="22.1.V2-1">
        <addPrimaryKey columnNames="school_id" 
                       constraintName="PRIMARY" 
                       tableName="school"/>
    </changeSet>
    <!-- 增加主键,联合主键 -->
    <changeSet author="cavan" id="22.1.V2-2">
        <addPrimaryKey columnNames="school_id, school_name" 
                       constraintName="PRIMARY" 
                       tableName="school"/>
    </changeSet>

    <!-- 创建索引,删除索引 -->
    <changeSet author="cavan" id="22.1.V2-3">
        <!-- 创建索引 -->
        <createIndex indexName="school_id" tableName="school">
            <column name="school"/>
        </createIndex>
        <!-- 删除索引 -->
        <dropIndex indexName="school_id" tableName="school"/>
    </changeSet>

    <!-- 增加外键约束 -->
    <changeSet author="cavan" id="22.1.V2-4">
        <addForeignKeyConstraint baseColumnNames="school_id" 
                                 baseTableName="school_class" 
                                 constraintName="school_class_ibfk_1" 
                                 deferrable="false" 
                                 initiallyDeferred="false" 
                                 onDelete="RESTRICT" 
                                 onUpdate="RESTRICT" 
                                 referencedColumnNames="school_id" 
                                 referencedTableName="school"/>
    </changeSet>

    <changeSet author="cavan" id="22.1.V2-5">
        <sql>
            ALTER  TABLE school MODIFY COLUMN last_modified_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
        </sql>
    </changeSet>


    <!-- ################### Column相关 ################### -->
    <!-- 增加字段 -->
    <changeSet author="cavan" id="22.1.V3-1">
        <addColumn tableName="school_class">       
            <column name="school_name" type="VARCHAR(100)">
                <constraints nullable="false"/>
            </column>
        </addColumn>
    </changeSet>

    <!-- 删除字段 -->
    <changeSet author="cavan" id="22.1.V3-2">
        <dropColumn tableName="school">  
            <column name="created_by"/>
            <column name="created_date"/>
            <column name="last_modified_by"/>
            <column name="last_modified_date"/>
        </dropColumn>	
    </changeSet>

    <!-- 修改字段 -->
    <changeSet author="cavan" id="22.1.V3-3">
        <!-- 修改字段名称( 其实可以连带类型一起修改了 ) -->
        <renameColumn tableName="school" oldColumnName="is_top_ten"
                      newColumnName="is_top_ten_new" columnDataType="varchar(20)"/>
        <!-- 修改字段类型 -->
        <modifyDataType tableName="school" columnName="school_name" newDataType="varchar(20)" />
    </changeSet>


    <!-- ################### 数据相关 ################### -->
    <!-- 增删改查数据 -->
    <changeSet author="cavan" id="22.1.V4-1">
        <insert tableName="school">
            <column name="school_id" value="2"/>
            <column name="school_name" value="qinghua"/>
            <column name="created_by" value="anonymity"/>
            <column name="created_date" valueDate="2021-07-20 15:51:53.0"/>
            <column name="last_modified_date" valueDate="2021-07-20 15:51:53.0"/>
            <column name="last_modified_by" value="anonymity"/>
        </insert>
    </changeSet>

    <changeSet author="cavan" id="22.1.V4-2">
        <delete tableName="school">
            <where>school_id='2'</where>
        </delete>
    </changeSet>

    <changeSet author="cavan" id="22.1.V4-3">
        <update tableName="school">
            <column name="school_name" value="beida"/>
            <where>school_id='2'</where>
        </update>
    </changeSet>

    <!-- 基于SQL语句 -->
    <changeSet author="cavan" id="22.1.V5-1">
        <sql>
            insert into school (school_id, school_name, is_top_ten) values (1, 'hafu', 1);
        </sql>
    </changeSet>

    <!-- 基于SQL文件 -->
    <changeSet author="cavan" id="22.1.V5-2">
        <sqlFile path="insert-data.sql"/>
    </changeSet>
</databaseChangeLog>

plugin

该插件可以根据数据库逆向生成 changlog 文件,从已有的数据库生成xml配置信息

  1. 生成xml文件

    通过idea的maven功能,找到 liquibase plugin,双击 liquibase:generateChangeLog 选项,执行完成之后就会在 properties 文件中配置的 outputChangeLogFile 路径生成对应的xml文件

  2. 生成数据库修改文档

    双击liquibase plugin面板中的liquibase:dbDoc选项,会生成数据库修改文档,默认会生成到target目录中

  3. 发布changelog
    之前我们对changelog的编辑都需要通过启动项目来运行changelog,有时候我们可能想不重启项目便能将修改发布运行到数据库中
    双击liquibase plugin面板中的liquibase:update选项,便可以将修改同步到数据库中

<!-- 接着引入liquibase插件, 如果你的数据库已经有表和数据了可以使用该插件反向生成xml文件 -->
<plugin>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-maven-plugin</artifactId>
    <version>3.5.1</version>
    <configuration>
        <propertyFile>src/main/resources/db/liquibase.properties</propertyFile>
        <propertyFileWillOverride>true</propertyFileWillOverride>
        <outputChangeLogFile>src/main/resources/liquibase/changelog/changelog_init.xml</outputChangeLogFile>
    </configuration>
</plugin>

基本规范和常见问题见:https://blog.csdn.net/weixin_42835409/article/details/129407613

拓展

liquibase管理数据库与tk.mybatis、mybatis-generator结合使用快速生成代码
见:https://blog.csdn.net/nb7474/article/details/83387101

参考地址

  • http://www.manongjc.com/detail/39-bphvettmfwuhycm.html
  • https://blog.csdn.net/weixin_42835409/article/details/129407613
  • https://www.kuangstudy.com/bbs/1486993129252241410
  • http://www.manongjc.com/detail/39-bphvettmfwuhycm.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值