概述
Liquibase是一个用于数据库重构和迁移的开源工具,通过日志文件的形式记录数据库的变更,然后执行日志文件中的修改,将数据库更新或回滚到一致的状态。它的目标是提供一种数据库类型无关的解决方案,通过执行schema类型的文件来达到迁移。其优点主要有以下:
- 支持几乎所有主流的数据库,如MySQL, PostgreSQL, Oracle, Sql Server, DB2等;
- 支持多开发者的协作维护;
- 日志文件支持多种格式,如XML, YAML, JSON, SQL等;
- 支持多种运行方式,如命令行、Spring集成、Maven插件、Gradle插件等对方的
理解databasechangelog和databasechangeloglock表
LiquiBase在执行changelog时,会在数据库中插入两张表:DATABASECHANGELOG
和DATABASECHANGELOGLOCK
,分别记录changelog的执行日志和锁日志。
LiquiBase在执行changelog中的changeSet时,会首先查看DATABASECHANGELOG
表,若是已经执行过,则会跳过(除非changeSet的runAlways
属性为true),若是没有执行过,则执行并记录changelog日志;
changelog中的一个changeSet对应一个事务,在changeSet执行完后commit,若是出现错误则rollback;
配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: root
username: root
url: jdbc:mysql://localhost:3306/study?useSSL=false&serverTimezone=UTC
liquibase:
change-log: classpath:changelog.xml
user: root
password: root
url: jdbc:mysql://localhost:3306/study?useSSL=false&serverTimezone=UTC
enabled: true
drop-first: false
依赖
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
changeLog:xml (**就是liquibase里的changelog概念,是可以多层嵌套引用其他changelog的**)
include: 引用changelog的标签
file: 引用changelog文件的质地
changeSet: 一个changeLog可以包含多个changeSet标签,每个changeSet都由id和author以及filepath属性进行唯一标识,当 Liquibase执行数据库changeLog时,它按顺序读取 changeSet,并针对每个changeSet检查databasechangelog表,以查看是否运行了 id/author/filepath的组合。如果已运行,则将跳过changeSet,除非存在真正的runAlways标签。运行changeSet中所有更改后,Liquibase将 databasechangelog中插入带有 id/author/filepath的新行以及changeSet的MD5Sum。每个changeSet 的事物是单独的,最佳做法是确保每个changeSet都尽可能原子性更改,以避免失败的结果使数据库中剩下的未处理的语句处于unknown 状态。
author:作者
id:changeSet的id(**最好用2019082600-01这种日期的格式,这样更清楚一点,如果是一个目标库可能会被多个项目的liquibase执行到的话,这个id会被写到同一个databasechangelog表 里,可以在日期前面加上项目名或者项目名的缩写来区分,比如reference-2019082600-01)
runAlways:执行每次运行时设置的更改,即使更改之前已运行
context:可以用于灵活控制脚本在哪些环境中执行,我们系统一般定义的是环境(比如team2,uat,prod),多个的话可以用 , or and分割,也支持!prod取反的形式 (**如果定义了context,执行时候就一定要指定contexts,不指定的话会取执行所有的sql,context就失效了)
comment: changeSet的说明。
preConditions:将执行changeSet之前必须通过的前提条件。可用于在做不可恢复的内容之执行数据健全性检查
rollback:描述如何回滚changeSet的 SQL 语句或重构标签
createTable: 创建表的标签
tableName:表名
column: 表字段的标签
name:字段名
type: 字段类型
remarks: 字段备注
constraints:字段约束
primaryKey: true代表是主键
nullable: true,false
unique: true,false
createIndex: 创建索引的标签
indexName:索引名
tableName:创建索引的表名
column: 索引字段的标签(多个代表联合索引)
name:字段名
addColumn: 增加字段的标签
tableName:增加字段的表名
column: 增加的字段
name:字段名
type: 字段类型
sql: sql标签,内容可以直接是sql语句
endDelimiter: 要应用于语句末尾的分隔符。默认值为;,可以设置为''。
splitStatement: true,false
stripComments: 设置为 true以在执行之前删除 SQL中的任何注释,否则为 false。如果
未设置,则默认值为false
comment:注释
sqlFile: 引用sql文件的标签
path:引用sql文件的地址
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd
http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
<!-- ################### Table相关 ################### -->
<!-- 创建表 -->
<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>