数据库版本控制liquibase

数据库版本控制liquibase

一、liquibase介绍

1、什么是liquibase

liquibase是一个数据库变更的版本控制工具。项目中通过liquibase解析用户编写的liquibase的配置文件,生成sql语句,并执行和记录。执行是根据记录确定sql语句是否曾经执行过,和配置文件里的预判断语句确定sql是否执行。liquibase开源版使用Apache 2.0协议。
官网地址:https://www.liquibase.org/

2、liquibase的优点

1、配置文件支持SQL、XML、JSON 或者 YAML2、版本控制按序执行3、可以用上下文控制sql在何时何地如何执行。4、支持schmea的变更5、根据配置文件自动生成sql语句用于预览6、可重复执行迁移7、可插件拓展8、可回滚9、可兼容14种主流数据库如oracle,mysql,pg等,支持平滑迁移10、支持schema方式的多租户(multi-tenant)

3、liquibase的基本概念

1、changeSet 执行sql的并记录、版本控制的最小单元。即每条changeSet生成1条执行记录,版本控制是基于执行记录的。 2、changelog 即执行记录。由changeSet执行后产生的记录。记录默认保存在databasechangelog表中,此表由liquibase自动生成。包含id,author,filename,dateexcuted,orderexcuted,exectype,md5sum等字段。 3、databasechangeloglock。liquibase的锁表。liquibase在执行前更新此表的locked为true,执行完liquibase的工作,将locked更新为false,适合集群使用

4、官网地址

https://www.liquibase.org/

5、Liquibase目录结构

Liquibase的changelog文件放在resources/db/changelog目录下,如下图所示,我们只需要追加更改这些文件来更新数据库。db.changelog-master.xml是主文件,只用于包含文件,一般不做更改。根据修改类型:增加,修改,删除表对象和数据导入分别在各自文件添加。数据文件以csv格式放到csv目录下。

二、liquibase使用

1、数据库配置
# 数据源
spring:
  datasource:
    pig:
      type: com.zaxxer.hikari.HikariDataSource
      driver-class-name: org.postgresql.Driver
      username: postgres
      password: 123456
      jdbc-url: jdbc:postgresql://${POSTGRE_HOST:pig-postgre}:${POSTGRE_PORT:5432}/${POSTGRE_DB:pig}?currentSchema=pig
      liquibase:
        enabled: true
        change-log: classpath:/db/liquibase/db.changelog-master.xml

db.changelog-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.4.xsd">

	<!-- 初始化数据库时候执行的 -->
	<includeAll path="./init-sql/" relativeToChangelogFile="true"/>

	<!-- sqlchangelog文件中存放迭代版本中产生的sql脚本-->
    <includeAll path="./sqlchangelog/" relativeToChangelogFile="true"/>

</databaseChangeLog>

2、数据库产生表

在这里插入图片描述

CREATE TABLE "pig_config"."databasechangelog" (
  "id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
  "author" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
  "filename" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
  "dateexecuted" timestamp(6) NOT NULL,
  "orderexecuted" int4 NOT NULL,
  "exectype" varchar(10) COLLATE "pg_catalog"."default" NOT NULL,
  "md5sum" varchar(35) COLLATE "pg_catalog"."default",
  "description" varchar(255) COLLATE "pg_catalog"."default",
  "comments" varchar(255) COLLATE "pg_catalog"."default",
  "tag" varchar(255) COLLATE "pg_catalog"."default",
  "liquibase" varchar(20) COLLATE "pg_catalog"."default",
  "contexts" varchar(255) COLLATE "pg_catalog"."default",
  "labels" varchar(255) COLLATE "pg_catalog"."default",
  "deployment_id" varchar(10) COLLATE "pg_catalog"."default"
)
;

ALTER TABLE "pig_config"."databasechangelog" 
  OWNER TO "cloud_dev_pg";
CREATE TABLE "pig_config"."databasechangeloglock" (
  "id" int4 NOT NULL,
  "locked" bool NOT NULL,
  "lockgranted" timestamp(6),
  "lockedby" varchar(255) COLLATE "pg_catalog"."default",
  CONSTRAINT "databasechangeloglock_pkey" PRIMARY KEY ("id")
)
;

ALTER TABLE "pig_config"."databasechangeloglock" 
  OWNER TO "cloud_dev_pg";
3、文件配置

如下所示SQL方式:

--liquibase formatted sql

--changeset test:20230607_1
-- 2023年6月7日  崔辛福 告警通知规则新增组织名称代码
ALTER TABLE "aio_event"."rule_config" ADD COLUMN org_name varchar(100);
COMMENT ON COLUMN "aio_event"."rule_config"."org_name" is '机构昵称';

--changeset test:20230612_1
-- 2023年6月12日  崔辛福 值守报告推送新增日周月cron表达式
ALTER TABLE "aio_event"."report_push_set" ADD COLUMN day_cron varchar(50);
COMMENT ON COLUMN "aio_event"."report_push_set"."day_cron" is '日报接收时间表达式';

ALTER TABLE "aio_event"."report_push_set" ADD COLUMN week_cron varchar(50);
COMMENT ON COLUMN "aio_event"."report_push_set"."week_cron" is '周报接收时间表达式';

ALTER TABLE "aio_event"."report_push_set" ADD COLUMN month_cron varchar(50);
COMMENT ON COLUMN "aio_event"."report_push_set"."month_cron" is '月报接收时间表达式';

说明例如:

–changeset test:20230612_1
– 2023年6月7日 说明
具体说明:
changset 为一条记录标识
test作者信息
20230612_1 执行id
– 2023年6月7日 为用户自己添加的注释,不会参与入库和其他操作,只是纯粹的注释一下

如下所示XML方式:

<?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>
4、使用方式代码

启动服务(AioDBApplication)连接的数据源会在数据库里面产生两张表:
databasechangelog
databasechangeloglock

● databaseChangeLog文件,Liquibase入口文件。这个文件里可以引入你的版本修改文件,执行顺序为从上到下。
● LiquiBase在执行changelog时,会在数据库中插入两张表:DATABASECHANGELOG和DATABASECHANGELOGLOCK,分别记录changelog的执行日志和锁日志。
● LiquiBase在执行changelog中的changeSet时,会首先查看DATABASECHANGELOG表,若是已经执行过,则会跳过(除非changeSet的runAlways属性为true),若是没有执行过,则执行并记录changelog日志;
● changelog中的一个changeSet对应一个事务,在changeSet执行完后commit,若是出现错误则rollback;
链接数据源代码:
DataSourceConfig.Java


import liquibase.integration.spring.SpringLiquibase;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * @author: test
 * @createTime: 2023年05月31日 16:58
 * @description: -
 */
@Configuration
public class DataSourceConfig {

	@Bean
	@ConfigurationProperties("spring.datasource.aio")
	public DataSource aioDataSource() {
		return DataSourceBuilder.create().build();
	}

	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.aio.liquibase")
	public LiquibaseProperties aioLiquibaseProperties() {
		return new LiquibaseProperties();
	}

	@Bean
	public SpringLiquibase aioLiquibase() {
		return createSpringLiquibase(aioDataSource(), aioLiquibaseProperties());
	}

	private static SpringLiquibase createSpringLiquibase(DataSource dataSource, LiquibaseProperties properties) {
		SpringLiquibase liquibase = new SpringLiquibase();
		liquibase.setDataSource(dataSource);
		liquibase.setChangeLog(properties.getChangeLog());
		liquibase.setContexts(properties.getContexts());
		liquibase.setDefaultSchema(properties.getDefaultSchema());
		liquibase.setDropFirst(properties.isDropFirst());
		liquibase.setShouldRun(properties.isEnabled());
		liquibase.setLabels(properties.getLabels());
		liquibase.setChangeLogParameters(properties.getParameters());
		liquibase.setRollbackFile(properties.getRollbackFile());
		return liquibase;
	}

}

可以在程序跑完结束掉:
如下代码所示

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;

import java.net.InetAddress;

/**
 * @Author: wb_fb132
 * @createTime: 2023年04月06日 15:37:19
 * @Description: 数据库变更的版本控制服务
 */
@SpringBootApplication
class AioDBApplication {

	private static final Logger logger = LoggerFactory.getLogger(AioDBApplication.class);

	public static void main(String[] args) throws Exception {
		SpringApplication app = new SpringApplication(AioDBApplication.class);
		ConfigurableApplicationContext context = app.run(args);
		Environment env = context.getEnvironment();
		String protocol = "http";
		if (env.getProperty("server.ssl.key-store") != null) {
			protocol = "https";
		}
		logger.info("\n----------------------------------------------------------\n\t" +
						"Application '{}' is running! Access URLs:\n\t" +
						"Local: \t\t{}://localhost:{}\n\t" +
						"External: \t{}://{}:{}\n\t" +
						"Profile(s): \t{}\n----------------------------------------------------------",
				env.getProperty("spring.application.name"),
				protocol,
				env.getProperty("server.port"),
				protocol,
				InetAddress.getLocalHost().getHostAddress(),
				env.getProperty("server.port"),
				env.getActiveProfiles());
		// 服务停止
		exitApplication(context);
	}

	public static void exitApplication(ConfigurableApplicationContext context) {
		try {
			Thread.sleep(3000);
			int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
			logger.info("\n----------------------------------------------------------\n\t" +
							"Application '{}' is exit succeeded!\n\t" +
							"ExitCode(s): \t{}\n----------------------------------------------------------",
					"aio-cloud-db",
					exitCode);
			System.exit(exitCode);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}
  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值