在多租户场景下,利用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和自己的业务表

相关推荐
brzhang14 分钟前
我写了个脚本,让AI每天自动看完热榜、写稿、配乐,还用我的声音读出来
前端·后端·架构
bobz96539 分钟前
vscode 使用 trunk code
后端
天天摸鱼的java工程师42 分钟前
你如何处理一个高并发接口的线程安全问题?说说你做过的优化措施
java·后端
JohnYan1 小时前
Bun技术评估 - 11 Websocket
javascript·后端·bun
HEY_FLYINGPIG1 小时前
【FLASK】FLASK应用中的多用户并行登录与加密
后端·python·flask
fydw_7151 小时前
Flask-login 处理授权逻辑
后端·python·flask
ZHOU_WUYI1 小时前
Flask 动态模块注册
后端·python·flask
Micro麦可乐1 小时前
最新Spring Security实战教程(十八)安全日志与审计:关键操作追踪与风险预警
java·spring boot·后端·安全·spring·安全审计
考虑考虑2 小时前
JDK17中的Sealed Classes
java·后端·java ee
前端日常开发2 小时前
0 费用使用免费服务器部署 NestJS 项目
前端·后端