通过liquibase实现一个简单的数据库适配器,自动适配60+数据库

1. 简介

最近我打算开发一个数据库适配器,核心目的是实现数据库表结构语句的动态执行,比如常见的创建/删除操作------涉及表、视图、索引、序列等各类数据库对象。当然,这个组件的理想状态是能自动适配主流数据库,用户无需纠结不同数据库的语法差异,只用专注于对应操作的数据结构定义就行。

之前写过一篇关于liquibase集成使用的文章,其在脚本支持和主流数据库适配(目前已支持超过60种数据库)方面有着不错的表现并且还能自动比较、追踪数据库的结构变化,用它来做核心组件,无疑能省下大量开发工作量,所以决定采用liquibase作为实现该功能的核心组件。

我们平时就常把liquibase用作项目的ddl版本管理工具,大多配置为项目启动阶段自动执行,把DDL相关信息持久化到数据库中。既然启动时能执行,那项目运行过程中想必也能通过编程方式触发执行------思路明确,话不多说,开整!

2. 环境信息

复制代码
liquibase: 4.33.0
java: 8

3. Maven

xml 复制代码
<properties>
  <logback.version>1.3.15</logback.version>
  <lombok.version>1.18.38</lombok.version>
  <junit.version>5.10.0</junit.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
    <version>4.33.0</version>
  </dependency>

  <dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.4.5</version>
  </dependency>

  <dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.4.0</version>
    <scope>provided</scope>
  </dependency>

  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.38</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>${logback.version}</version>
    <optional>true</optional>
  </dependency>

  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.9</version>
  </dependency>

  <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
  </dependency>

</dependencies>

4. Quick Start

liquibase数据结构分析

参考一下liquibase xml 描述信息如下

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:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.6.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">
    <changeSet author="ludangxin" id="1662615627445-2">
        <createTable tableName="role" remarks="角色信息表">
            <column name="name" remarks="角色名称" type="VARCHAR(255)"/>
            <column name="role_key" remarks="角色key" type="VARCHAR(255)"/>
        </createTable>
        <dropTable tableName="order"/>
    </changeSet>
    
    <changeSet author="ludangxin" id="1662615627445-3">
        <createTable tableName="user" remarks="用户信息表">
            <column name="id" type="INT" remarks="主键">
                <constraints nullable="false" primaryKey="true"/>
            </column>
            <column name="username" type="VARCHAR(255)" remarks="用户名称"/>
            <column name="password" type="VARCHAR(255)" remarks="密码"/>
            <column name="age" type="INT" remarks="性别"/>
            <column name="sex" type="VARCHAR(255)" remarks="性别"/>
            <column name="role" type="VARCHAR(255)" remarks="角色"/>
            <column name="create_time" type="DATETIME" defaultValueComputed="NOW()" remarks="创建时间"/>
        </createTable>
    </changeSet>
</databaseChangeLog>

可以从上述的xml中看到 最外层到里依次是DatabaseChangeLog → ChangeSet → Change(createTable、dropTable....) , 且都是一对多的关系 基本的结构理清楚之后, 我们使用这些对象实现一个创建表的功能

java 复制代码
@Test
public void test() throws SQLException, LiquibaseException {
    DatabaseChangeLog changeLog = new DatabaseChangeLog();
    changeLog.setLogicalFilePath("testPath");
    final CreateTableChange createTableChange = buildCreateTableChange();
    ChangeSet changeSet = new ChangeSet("123", "sysadmin", false, false, "system", null, null, changeLog);
    changeSet.addChange(createTableChange);
    changeLog.addChangeSet(changeSet);
    final Database database = buildDatabase("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true", "root", "123456");
    Liquibase liquibase = new Liquibase(changeLog, new ClassLoaderResourceAccessor(), database);
    liquibase.update(new Contexts(), new LabelExpression());
}

public static CreateTableChange buildCreateTableChange() {
    CreateTableChange createTableChange = new CreateTableChange();
    createTableChange.setSchemaName("test");
    createTableChange.setTableName("test_table");
    createTableChange.setRemarks("测试表");
    final ColumnConfig columnConfig = new ColumnConfig();
    columnConfig.setName("name");
    columnConfig.setType("varchar(255)");
    columnConfig.setRemarks("姓名");
    createTableChange.addColumn(columnConfig);
    return createTableChange;
}

public static Database buildDatabase(String url, String username, String password) throws DatabaseException, SQLException {
    Connection connection = DriverManager.getConnection(url, username, password);
    return DatabaseFactory.getInstance()
                          .findCorrectDatabaseImplementation(new JdbcConnection(connection));
}

测试执行结果如下

不仅创建了目标表, 还生成了liquibase元信息存储和悲观锁表(防止并发执行导致表锁)

刚刚尝试创建了一张新表, 那除了创建表, 它还能干嘛呢? 看一下liquibase源码

可以看到, 对库/表的操作基本上都覆盖了, 那其实就好办了 对这些change对象做简单的封装, 方便客户端调用即可

5. 封装工具类

封装思路, 将这些change对应的功能封装一个builder类 用于方便build, 比如

这里只展示核心类, 其余代码请参考最后的源码

5.1 CreateTableChangeBuilder

创建表change builder

java 复制代码
import com.ldx.meta.db.engine.entity.Column;
import com.ldx.meta.db.engine.entity.Table;
import liquibase.change.ColumnConfig;
import liquibase.change.ConstraintsConfig;
import liquibase.change.core.CreateTableChange;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.apache.commons.collections4.CollectionUtils;

import java.util.List;

/**
 * 建表 change builder
 *
 * @author ludangxin
 * @since  2025/11/6
 */
@Data
@Accessors(fluent = true)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CreateTableChangeBuilder {
    private String schemaName;

    private Table table;

    private boolean ifNotExists = true;

    public static CreateTableChangeBuilder getInstance() {
        return new CreateTableChangeBuilder();
    }

    public CreateTableChange build() {
        final String tableName = table.getName();
        final String tableComment = table.getComment();
        CreateTableChange createTableChange = new CreateTableChange();
        createTableChange.setSchemaName(schemaName);
        createTableChange.setTableName(tableName);
        createTableChange.setIfNotExists(ifNotExists);
        createTableChange.setRemarks(tableComment);
        final List<Column> columns = table.getColumns();

        if (CollectionUtils.isNotEmpty(columns)) {
            for (Column column : columns) {
                final ColumnConfig columnConfig = buildColumnConfig(column);
                createTableChange.addColumn(columnConfig);
            }
        }

        return createTableChange;
    }

    private static ColumnConfig buildColumnConfig(Column column) {
        ColumnConfig columnConfig = new ColumnConfig();
        columnConfig.setName(column.getName());
        columnConfig.setType(column.getType());
        columnConfig.setRemarks(column.getComment());
        columnConfig.setDefaultValue(column.getDefaultValue());
        final ConstraintsConfig constraintsConfig = new ConstraintsConfig();
        constraintsConfig.setNullable((!column.isNotNull()));
        constraintsConfig.setPrimaryKey(column.isPrimaryKey());
        columnConfig.setConstraints(constraintsConfig);
        return columnConfig;
    }
}

5.2 ChangeSetBuilder

change 集合 builder: 用于快速收集change

java 复制代码
import com.ldx.meta.db.engine.configuration.MetaDbProperties;
import liquibase.change.Change;
import liquibase.changelog.ChangeSet;
import liquibase.changelog.DatabaseChangeLog;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * change set
 *
 * @author ludangxin
 * @since 2025/11/6
 */
@Setter
@Accessors(fluent = true)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ChangeSetBuilder {
    private String id;

    private String comments;

    private String author;

    private boolean alwaysRun = false;

    private boolean runOnChange = false;

    private String filePath = "none";

    private String contextFilter;

    private String dbmsList;

    private DatabaseChangeLog changeLog;

    private final List<Change> changes = new ArrayList<>();

    private MetaDbProperties properties = MetaDbProperties.DEFAULT();

    public static ChangeSetBuilder getInstance() {
        return new ChangeSetBuilder();
    }

    public ChangeSetBuilder change(Change... change) {
        changes.addAll(new ArrayList<>(Arrays.asList(change)));

        return this;
    }

    public ChangeSet build() {
        if (changeLog == null) {
            changeLog = ChangeLogBuilder.getInstance()
                                        .build();
        }

        if (properties != null) {
            if (id == null || id.isEmpty()) {
                id = properties.getIdProvider()
                               .getNextId();
            }

            if (author == null || author.isEmpty()) {
                author = properties.getAuthor();
            }
        }

        changeLog.setLogicalFilePath(filePath);
        ChangeSet changeSet = new ChangeSet(id, author, alwaysRun, runOnChange, filePath, contextFilter, dbmsList, changeLog);
        changeSet.setComments(comments);
        changeSet.setLogicalFilePath(filePath);

        for (Change change : changes) {
            changeSet.addChange(change);
        }

        return changeSet;
    }
}

5.3 ChangeLogBuilder

changeLog: 用于快速收集changeSet

java 复制代码
import liquibase.changelog.ChangeSet;
import liquibase.changelog.DatabaseChangeLog;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * change log
 *
 * @author ludangxin
 * @since  2025/11/6
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ChangeLogBuilder {

    public static ChangeLogBuilder getInstance() {
        return new ChangeLogBuilder();
    }

    private final List<ChangeSet> changeSets = new ArrayList<>();

    public ChangeLogBuilder changeSet(ChangeSet... changeSet) {
        changeSets.addAll(new ArrayList<>(Arrays.asList(changeSet)));

        return this;
    }

    public DatabaseChangeLog build() {
        DatabaseChangeLog changeLog = new DatabaseChangeLog();

        if (!changeSets.isEmpty()) {
            final ChangeSet anyChangeSet = changeSets.get(0);
            final String logicalFilePath = anyChangeSet.getLogicalFilePath();
            changeLog.setLogicalFilePath(logicalFilePath);
        }
        else {
            changeLog.setLogicalFilePath("default");
        }

        for (ChangeSet changeSet : changeSets) {
            changeLog.addChangeSet(changeSet);
        }

        return changeLog;
    }
}

5.4 MetaDbEngineService

适配器执行入口 支持快速execute & lambda调用

java 复制代码
import com.ldx.meta.db.engine.configuration.MetaDbDataSource;
import com.ldx.meta.db.engine.configuration.MetaDbProperties;
import com.ldx.meta.db.engine.exception.MetaDbDataSourceException;
import com.ldx.meta.db.engine.exception.MetaDbEngineException;
import com.ldx.meta.db.engine.builder.ChangeLogBuilder;
import com.ldx.meta.db.engine.builder.ChangeSetBuilder;
import com.ldx.meta.db.engine.builder.SqlBuilder;
import liquibase.Contexts;
import liquibase.LabelExpression;
import liquibase.Liquibase;
import liquibase.change.Change;
import liquibase.changelog.ChangeSet;
import liquibase.changelog.DatabaseChangeLog;
import liquibase.database.Database;
import liquibase.exception.DatabaseException;
import liquibase.exception.LiquibaseException;
import liquibase.resource.ClassLoaderResourceAccessor;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 适配器入口
 *
 * @author ludangxin
 * @since 2025/11/6
 */
@Slf4j
public class MetaDbEngineService {
    private final MetaDbDataSource dataSource;

    private final MetaDbProperties metaDbProperties;

    public MetaDbEngineService(MetaDbDataSource dataSource) {
        this.dataSource = Objects.requireNonNull(dataSource, "dataSource can not be null");
        this.metaDbProperties = MetaDbProperties.DEFAULT();
    }

    public MetaDbEngineService(MetaDbDataSource dataSource, MetaDbProperties metaDbProperties) {
        this.dataSource = Objects.requireNonNull(dataSource, "dataSource can not be null");
        this.metaDbProperties = Objects.requireNonNull(metaDbProperties, "metaDbProperties can not be null");
    }

    public void execute(Change... changes) {
        this.execute(false, changes);
    }

    public void execute(boolean printSql, Change... changes) {
        if (changes == null || changes.length == 0) {
            log.warn("execute called with no changes -> no-op");
            return;
        }

        final ChangeSet changeSet = ChangeSetBuilder.getInstance()
                                                    .properties(metaDbProperties)
                                                    .change(changes)
                                                    .build();
        this.execute(printSql, changeSet);
    }

    public void execute(ChangeSet... changeSets) {
        this.execute(false, changeSets);
    }

    public void execute(boolean printSql, ChangeSet... changeSets) {
        if (changeSets == null || changeSets.length == 0) {
            log.warn("execute called with no changeSets -> no-op");
            return;
        }

        final DatabaseChangeLog changeLog = ChangeLogBuilder.getInstance()
                                                            .changeSet(changeSets)
                                                            .build();
        this.execute(printSql, changeLog);
    }

    public void execute(DatabaseChangeLog changeLog) {
        this.execute(false, changeLog);
    }

    public void execute(boolean printSql, DatabaseChangeLog changeLog) {
        if (changeLog == null || CollectionUtils.isEmpty(changeLog.getChangeSets())) {
            log.warn("execute called with empty changeLog -> no-op");
            return;
        }

        this.executeChangeLog(changeLog, printSql);
    }

    public List<String> getSqlList(Change... changes) {
        if (changes == null || changes.length == 0) {
            return Collections.emptyList();
        }

        final ChangeSet changeSet = ChangeSetBuilder.getInstance()
                                                    .properties(metaDbProperties)
                                                    .change(changes)
                                                    .build();
        return this.getSqlList(changeSet);
    }

    public List<String> getSqlList(ChangeSet... changeSets) {
        if (changeSets == null || changeSets.length == 0) {
            return Collections.emptyList();
        }

        final DatabaseChangeLog changeLog = ChangeLogBuilder.getInstance()
                                                            .changeSet(changeSets)
                                                            .build();
        return this.getSqlList(changeLog);
    }

    public List<String> getSqlList(DatabaseChangeLog changeLog) {
        if (changeLog == null) {
            return Collections.emptyList();
        }

        return this.generateSqlList(changeLog);
    }

    public String getSql(Change... changes) {
        List<String> list = getSqlList(changes);
        return list.isEmpty() ? "" : String.join(";\n", list) + ";";
    }

    public String getSql(ChangeSet... changeSets) {
        List<String> list = getSqlList(changeSets);
        return list.isEmpty() ? "" : String.join(";\n", list) + ";";
    }

    public String getSql(DatabaseChangeLog changeLog) {
        List<String> list = getSqlList(changeLog);
        return list.isEmpty() ? "" : String.join(";\n", list) + ";";
    }

    private void executeChangeLog(DatabaseChangeLog changeLog, boolean printSql) {
        try (final Database database = dataSource.openDatabase()) {
            if (printSql) {
                this.printSql(database, changeLog);
            }
            String url = safeGetDbUrl(database);
            Liquibase liquibase = new Liquibase(changeLog, new ClassLoaderResourceAccessor(), database);
            liquibase.update(new Contexts(), new LabelExpression());
            int executed = changeLog.getChangeSets() == null ? 0 : changeLog.getChangeSets()
                                                                            .size();
            log.info("liquibase execute success, URL: {}, changeSets: {}", url, executed);
        }
        catch (LiquibaseException e) {
            throw new MetaDbEngineException("liquibase execute failed: " + e.getMessage(), e, safeGenerateAllSql(changeLog));
        }
        catch (RuntimeException e) {
            throw new MetaDbEngineException("meta-db-engine execute changelog failed: " + e.getMessage(), e, safeGenerateAllSql(changeLog));
        }
    }

    private void printSql(DatabaseChangeLog changeLog) {
        try (final Database database = dataSource.openDatabase()) {
            printSql(database, changeLog);
        }
        catch (DatabaseException e) {
            throw new MetaDbDataSourceException("print sql failed", e);
        }
    }

    public void printSql(Database database, DatabaseChangeLog changeLog) {
        final String dbUrl = safeGetDbUrl(database);
        generateSqlList(changeLog).forEach(sql -> log.info("execute sql url:{} statement: {}", dbUrl, sql));
    }

    private List<String> generateSqlList(DatabaseChangeLog changeLog) {
        try (final Database database = dataSource.openDatabase()) {
            if (changeLog == null || CollectionUtils.isEmpty(changeLog.getChangeSets())) {
                return Collections.emptyList();
            }
            return changeLog.getChangeSets()
                            .stream()
                            .filter(Objects::nonNull)
                            .map(ChangeSet::getChanges)
                            .filter(CollectionUtils::isNotEmpty)
                            .flatMap(Collection::stream)
                            .map(change -> SqlBuilder.getInstance()
                                                     .database(database)
                                                     .change(change)
                                                     .build())
                            .collect(Collectors.toList());
        }
        catch (Exception e) {
            throw new MetaDbEngineException("generate sql failed", e, null);
        }
    }

    private List<String> safeGenerateAllSql(DatabaseChangeLog changeLog) {
        try {
            return this.generateSqlList(changeLog);
        }
        catch (Exception e) {
            return Collections.emptyList();
        }
    }

    private String safeGetDbUrl(Database db) {
        try {
            return db != null && db.getConnection() != null ? db.getConnection()
                                                                .getURL() : "unknown";
        }
        catch (Exception e) {
            return "unknown";
        }
    }

    public TransientEngine lambda() {
        return new TransientEngine(this);
    }

    @Setter
    @Accessors(fluent = true)
    public static final class TransientEngine {
        private final MetaDbEngineService engine;

        private boolean printSql = false;

        private DatabaseChangeLog changeLog = ChangeLogBuilder.getInstance()
                                                              .build();

        private TransientEngine(MetaDbEngineService service) {
            this.engine = service;
        }

        public TransientEngine change(Change... changes) {
            final ChangeSet changeSet = ChangeSetBuilder.getInstance()
                                                        .properties(engine.metaDbProperties)
                                                        .change(changes)
                                                        .build();
            return this.changeSet(changeSet);
        }

        public TransientEngine changeSet(ChangeSet... changeSets) {
            for (ChangeSet changeSet : changeSets) {
                changeLog.addChangeSet(changeSet);
            }

            return this;
        }

        public TransientEngine changeLog(DatabaseChangeLog changeLog) {
            Objects.requireNonNull(changeLog, "changeLog can not be null");
            this.changeLog = changeLog;
            return this;
        }

        public void execute() {
            engine.execute(printSql, changeLog);
        }

        public void printSql() {
            engine.printSql(changeLog);
        }

        public List<String> getSqlList() {
            return engine.getSqlList(changeLog);
        }
    }
}

6. 测试工具类

java 复制代码
import com.ldx.meta.db.engine.builder.ChangeSetBuilder;
import com.ldx.meta.db.engine.builder.CreateTableIndexChangeBuilder;
import com.ldx.meta.db.engine.builder.DropTableIndexChangeBuilder;
import com.ldx.meta.db.engine.builder.DropViewChangeBuilder;
import com.ldx.meta.db.engine.configuration.MetaDbDataSource;
import com.ldx.meta.db.engine.configuration.MetaDbProperties;
import com.ldx.meta.db.engine.entity.Column;
import com.ldx.meta.db.engine.entity.Table;
import com.ldx.meta.db.engine.builder.CreateTableChangeBuilder;
import com.ldx.meta.db.engine.builder.CreateViewChangeBuilder;
import com.ldx.meta.db.engine.builder.DropTableChangeBuilder;
import com.ldx.meta.db.engine.builder.SqlBuilder;
import com.ldx.meta.db.engine.entity.TableIndex;
import com.ldx.meta.db.engine.enums.IdProviderType;
import liquibase.change.core.CreateIndexChange;
import liquibase.change.core.CreateTableChange;
import liquibase.change.core.CreateViewChange;
import liquibase.change.core.DropTableChange;
import liquibase.change.core.DropViewChange;
import liquibase.change.core.RawSQLChange;
import liquibase.changelog.ChangeSet;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import java.util.Arrays;
import java.util.Collections;

/**
 * test
 *
 * @author ludangxin
 * @since 2025/11/6
 */
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MetaDbEnginesTest {
    private static MetaDbEngineService META_DB_ENGINE_SERVICE;

    @BeforeAll
    public static void init() {
        final MetaDbDataSource metaDbDataSource = MetaDbDataSource.fromJdbc("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true", "root", "123456");
        final MetaDbProperties metaDbProperties = new MetaDbProperties();
        metaDbProperties.setIdProvider(IdProviderType.TIMESTAMP);
        metaDbProperties.setAuthor("zhang-tie-niu");
        META_DB_ENGINE_SERVICE = new MetaDbEngineService(metaDbDataSource, metaDbProperties);
    }

    @Test
    @Order(1)
    public void given_table_info_when_create_table_then_complete() {
        final Column idColumn = Column.builder()
                                      .name("id")
                                      .type("varchar(32)")
                                      .comment("主键")
                                      .build();
        final Table table = Table.builder()
                                 .name("test123")
                                 .columns(Collections.singletonList(idColumn))
                                 .build();
        final CreateTableChange createTableChange = CreateTableChangeBuilder.getInstance()
                                                                            .table(table)
                                                                            .build();

        META_DB_ENGINE_SERVICE.execute(createTableChange);
    }

    @Test
    @Order(2)
    public void given_view_info_when_create_view_then_complete() {
        final CreateViewChange createView = CreateViewChangeBuilder.getInstance()
                                                                   .viewName("v_test123")
                                                                   .sql("select * from test123")
                                                                   .replaceIfExists(true)
                                                                   .build();
        META_DB_ENGINE_SERVICE.lambda()
                              .printSql(true)
                              .change(createView)
                              .execute();
    }

    @Test
    @Order(3)
    public void given_sql_info_when_update_table_then_complete() {
        final RawSQLChange build = SqlBuilder.build("insert into test123 (id) values (1)");
        META_DB_ENGINE_SERVICE.lambda()
                              .printSql(true)
                              .change(build)
                              .execute();
    }

    @Test
    @Order(4)
    public void given_view_name_when_drop_view_then_complete() {
        final DropViewChange dropView = DropViewChangeBuilder.getInstance()
                                                             .viewName("v_test123")
                                                             .build();
        META_DB_ENGINE_SERVICE.lambda()
                              .printSql(true)
                              .change(dropView)
                              .execute();
    }

    @Test
    @Order(5)
    public void given_table_name_when_drop_table_then_complete() {
        final DropTableChange dropTable = DropTableChangeBuilder.getInstance()
                                                                .tableName("test123")
                                                                .build();
        META_DB_ENGINE_SERVICE.lambda()
                              .printSql(true)
                              .change(dropTable)
                              .execute();
    }

    @Test
    public void given_ddl_info_when_execute_then_complete() {
        final Column idColumn = Column.builder()
                                      .name("id")
                                      .type("varchar(32)")
                                      .primaryKey(true)
                                      .comment("主键")
                                      .build();
        final Column nameColumn = Column.builder()
                                        .name("name")
                                        .type("varchar(32)")
                                        .comment("姓名")
                                        .build();
        final Column idCardColumn = Column.builder()
                                          .name("id_card")
                                          .type("varchar(32)")
                                          .comment("身份证")
                                          .build();
        final Table table = Table.builder()
                                 .name("test666")
                                 .columns(Arrays.asList(idColumn, nameColumn, idCardColumn))
                                 .build();
        final CreateTableChange createTableChange = CreateTableChangeBuilder.getInstance()
                                                                            .table(table)
                                                                            .build();
        final TableIndex tableIdCardIndex = TableIndex.builder()
                                                      .indexName("id_card_unique_index")
                                                      .tableName("test666")
                                                      .columnNames(Collections.singletonList("id_card"))
                                                      .unique(true)
                                                      .build();
        final CreateIndexChange createIndexChange = CreateTableIndexChangeBuilder.getInstance()
                                                                                 .tableIndex(tableIdCardIndex)
                                                                                 .build();
        final ChangeSet changeSet = ChangeSetBuilder.getInstance()
                                                    .change(createTableChange)
                                                    .change(createIndexChange)
                                                    .build();
        META_DB_ENGINE_SERVICE.execute(changeSet);
    }

}

测试执行结果如下

7. 小结

本章节尝试使用liquibase作为数据库适配器底座, 通过简单封装其内置的DatabaseChangeLog → ChangeSet → Change(createTable、dropTable....)等对象, 实现方便快速的执行ddl操作, 最主要的是其默认就支持多达60+数据库的自动适配, 可以轻松的实现数据库兼容.

8. 源码

测试过程中的代码已全部上传至github, 欢迎点赞收藏 仓库地址: https://github.com/ludangxin/meta-db-engine

相关推荐
Thinking in Coder9 个月前
flowable适配达梦数据库
信创·flowable·国产化·达梦数据库·dm·liquibase·flowable适配
颜淡慕潇1 年前
【数据库系列】Liquibase 在 Spring Boot 中的使用--详细介绍
数据库·spring boot·后端·mysql·spring·liquibase
yicj1 年前
Springboot集成Liquibase笔记整理
java·spring boot·liquibase
Json____2 年前
spring boot 集成 flyway依赖 做数据库迁移,让部署没烦恼
spring boot·mysql·oracle·flyway·数据迁移·数据库迁移·liquibase