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

1. 简介

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

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

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

2. 环境信息

liquibase: 4.33.0 java: 8 

3. Maven

<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 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....) , 且都是一对多的关系 基本的结构理清楚之后, 我们使用这些对象实现一个创建表的功能

@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实现一个简单的数据库适配器,自动适配60+数据库

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

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

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

5. 封装工具类

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

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

5.1 CreateTableChangeBuilder

创建表change builder

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

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

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调用

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. 测试工具类

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);     }  } 

测试执行结果如下

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

7. 小结

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

8. 源码

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

发表评论

评论已关闭。

相关文章