在多租户场景下,利用liquibase实现数据库版本管理

摘要 :在SpringBoot的默认单一数据源下,liquibase能够进行数据库版本管理,如果是多租户情况下,则需要手动实现,本文主要介绍如何利用liquibase来实现多租户场景下的数据库版本管理。

简介

本文介绍的多租户场景,是多个用户数据库,使用的是同一个服务实例,然后通过header传递的tenant-id来区分具体使用哪一个数据库实例(如何实现多租户就不展开了,其他文章细说)。而数据库的版本管理,则需要统一进行管理,利用了SpringLiquibase类提供的管理能力,Hikari的数据库连接池。

配置说明

pom.xml文件

xml 复制代码
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
        </dependency>
    </dependencies>

resource目录下的配置

mysql.xml

该文件配置数据库版本更新语句的目录

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

    <!--  初始化表结构  -->
    <include file="/liquibase/mysql/*.sql"/>
</databaseChangeLog>

init.sql

sql 复制代码
--liquibase formatted sql

--changeset nise:202311201420
CREATE TABLE `USER` (
  `ID` bigint(20) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`ID`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户';

LiquibaseDataSourceProperties 属性配置类

arduino 复制代码
@Data
public class LiquibaseDataSourceProperties {

    /** 驱动名称 */
    private String driverClassName;
    /** 链接地址 */
    private String jdbcUrl;
    /** 用户名 */
    private String username;
    /** 密码 */
    private String password;
    /** 数据库schema */
    private String schema;

}

schema说明

也可以理解为数据库的隔离一种方式,只不过每一种数据库的表现形式不一样

  • mysqlschema和数据库的名称一样,同一个名称下只有一个库。
  • oracleschema就是数据库名称,oracle一般采用服务名连接的。
  • kingbaseschema是数据库里面的具体空间名称,比如public,如果使用该方式实现多租户,则可以在同一个库里面通过命名空间区分。

LiquibaseService

管理服务,这里简单写一下,最好写到try cache finally中来关闭数据库连接,防止失败。

scss 复制代码
@Service
public class LiquibaseService {

    @Autowired
    private ResourceLoader resourceLoader;

    /**
     * 初始化数据库
     * @param properties
     */
    public void init(LiquibaseDataSourceProperties properties) throws Exception{
        HikariDataSource hikariDataSource = this.buildDataSource(properties);
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(hikariDataSource);
        liquibase.setResourceLoader(resourceLoader);
        //指定changelog的位置,这里使用的一个master文件引用其他文件的方式
        liquibase.setChangeLog("classpath*:liquibase/mysql.xml");
        liquibase.setShouldRun(true);
        liquibase.afterPropertiesSet();
        hikariDataSource.close();
    }

    private HikariDataSource buildDataSource(LiquibaseDataSourceProperties properties){
        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setDriverClassName(properties.getDriverClassName());
        hikariDataSource.setJdbcUrl(properties.getJdbcUrl());
        hikariDataSource.setUsername(properties.getUsername());
        hikariDataSource.setPassword(properties.getPassword());
        hikariDataSource.setSchema(properties.getSchema());
        hikariDataSource.setMinimumIdle(1);
        hikariDataSource.setMaximumPoolSize(1);
        return hikariDataSource;
    }

}

服务调用

一般采用的是多数据源方式,或者采用从缓存中读取多租户的配置信息,然后初始化数据库;下面我就手动调用来模拟实现。

less 复制代码
@RestController
@RequestMapping(value = "liquibase")
public class LiquibaseController {

    @Autowired
    private LiquibaseService liquibaseService;

    @PostMapping(value = "init")
    public String init(@RequestBody LiquibaseDataSourceProperties properties) throws Exception{
        liquibaseService.init(properties);
        return "SUCCESS";
    }

}

结果

最终会生成两张数据库管理表DATABASECHANGELOGDATABASECHANGELOGLOCK和自己的业务表

相关推荐
Codebee1 小时前
Qoder CLI 与 OneCode 平台深度整合技术实践:CLI委托驱动的开发范式革新
后端
码事漫谈1 小时前
C++程序执行起点不是main:颠覆你认知的真相
后端
可观测性用观测云1 小时前
玩转 Pipelines 之修正链路错误状态码
后端
码事漫谈1 小时前
C++26:开启新纪元
后端
q***07142 小时前
Java实战:Spring Boot application.yml配置文件详解
java·网络·spring boot
龙卷风04052 小时前
深入理解Spring AI Alibaba多Agent系统:图结构驱动的智能协作
人工智能·后端
用户8356290780512 小时前
C# 高效生成 Word 表格:复杂表格创建实战指南
后端·c#
q***42822 小时前
SpringCloudGateWay
android·前端·后端
我是小妖怪,潇洒又自在2 小时前
springcloud alibaba搭建
后端·spring·spring cloud
l***74942 小时前
springboot与springcloud对应版本
java·spring boot·spring cloud