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

相关推荐
Asthenia04126 分钟前
ES-Java:一网打尽SearchRequest/SearchSourceBuilder/BoolQueryBuilder/QueryBuilders
后端
Aska_Lv29 分钟前
业务架构设计---硬件设备监控指标数据上报业务Java企业级架构
后端·架构
m0_7482552639 分钟前
Spring Boot 3.x 引入springdoc-openapi (内置Swagger UI、webmvc-api)
spring boot·后端·ui
小华同学ai44 分钟前
吊打中文合成!这款开源语音神器效果炸裂,逼真到离谱!
前端·后端·github
语落心生1 小时前
算法计算与训练如何支持低开销流式计算? deepseek背后的smallpond需要些新改造
后端
uhakadotcom1 小时前
Python高并发实战:阿里云函数计算 + 异步编程高效处理万人请求
后端·面试·github
uhakadotcom1 小时前
Apache Flink:实时数据处理的强大工具
后端·面试·github
INSO1 小时前
Docker Compose
后端
uhakadotcom2 小时前
了解Nginx替代品:选择合适的Web服务器
后端·面试·github
RisingWave中文开源社区2 小时前
经验分享|用开源产品构建一个高性能实时推荐引擎
数据库·后端·开源