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