Dm8+jpa整合liquibase框架

一、liquibase是什么

LiquiBase是一个用于数据库重构和迁移的开源工具,通过日志文件的形式记录数据库的变更,然后执行日志文件中的修改,将数据库更新或回滚到一致的状态。它的目标是提供一种数据库类型无关的解决方案,通过执行schema类型的文件来达到迁移。
其有点主要有以下:

  • 支持几乎所有主流的数据库,如MySQL, PostgreSQL, Oracle, Sql Server, DB2等;
  • 支持多开发者的协作维护;
  • 日志文件支持多种格式,如XML, YAML, JSON, SQL等;
  • 支持多种运行方式,如命令行、Spring集成、Maven插件、Gradle插件等。

liquibase 官方文档地址:hhttps://www.liquibase.org/
liquibase 源码下载地址:https://github.com/liquibase/liquibase

版本说明

本次测试使用时DM8版本+liquibase的3.8.10版本。

二、整合liquibase报:dm.jdbc.driver.DMException: 数据类型不匹配

这是因为:整合liquibase会初始化两张表,因为框架不支持所以生成的sql不对,从而导致报错。
在这里插入图片描述

三、调整liquibase的源码

下载liquibase源码:https://gitee.com/mirrors_addons/liquibase/tree/v3.8.9/或者去https://github.com/liquibase/liquibase下载

核心思路

继承OracleDatabase实现类复写端口跟驱动包名的部分,以及mustQuoteObjectName方法,加入关键字过滤。(不采用网上的继承AbstractJdbcDatabase类模仿Oracle类的方法去写,因为代码的其他部分有很多对oracle的判断,多达三十多处左右)

1. 新建DM8Database

新建DM8Database类并集成OracleDatabase,并重写以下方法。

package liquibase.database.core;

import liquibase.CatalogAndSchema;
import liquibase.Scope;
import liquibase.database.AbstractJdbcDatabase;
import liquibase.database.DatabaseConnection;
import liquibase.database.OfflineConnection;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.exception.ValidationErrors;
import liquibase.executor.ExecutorService;
import liquibase.logging.LogService;
import liquibase.logging.LogType;
import liquibase.statement.DatabaseFunction;
import liquibase.statement.SequenceCurrentValueFunction;
import liquibase.statement.SequenceNextValueFunction;
import liquibase.statement.core.RawCallStatement;
import liquibase.statement.core.RawSqlStatement;
import liquibase.structure.DatabaseObject;
import liquibase.structure.core.Catalog;
import liquibase.structure.core.Index;
import liquibase.structure.core.PrimaryKey;
import liquibase.structure.core.Schema;
import liquibase.util.JdbcUtils;
import liquibase.util.StringUtils;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.ResourceBundle.getBundle;


public class DM8Database extends OracleDatabase {
    private static final String PRODUCT_NAME = "DM DBMS";
    private static final String PRODUCT_SHORT_NAME = "DM DBMS";
    private static final String DRIVER_CONN_PERFIX = "jdbc:dm";
    private static final String DRIVER_STRING = "dm.jdbc.driver.DmDriver";
    private static final List QUOTE_KEYWORDS = Arrays.asList("ABORT", "ABSTRACT", "ACCESSED", "ACCOUNT", "ACROSS", "ACTION", "AFTER", "AGGREGATE", "ALLOW_DATETIME", "ALLOW_IP", "ANALYZE", "ARCHIVE", "ARCHIVEDIR", "ARCHIVELOG", "ARCHIVESTYLE", "ARRAYLEN", "ASENSITIVE", "ASYNCHRONOUS", "AT", "ATTACH", "AUTO", "AUTOEXTEND", "AUTONOMOUS_TRANSACTION", "AVG", "BACKED", "BACKUP", "BACKUPDIR", "BACKUPINFO", "BACKSET", "BADFILE", "BAKFILE", "BASE", "BEFORE", "BEGIN", "BIGINT", "BINARY", "BIT", "BITMAP", "BLOB", "BLOCK", "BOOLEAN", "BRANCH", "BTREE", "BUFFER ", "BUILD ", "BULK", "CACHE", "CALCULATE", "CASCADE", "CASCADED", "CATALOG", "CHAIN", "CHARACTER", "CHARACTERISTICS", "CIPHER", "CLOB", "CLOSE", "COLLATE", "COLLECT", "COLUMNS", "COMMITTED", "COMPILE", "COMPLETE", "COMPRESS", "COMPRESSED", "CONNECT_BY_IS_CYCLE", "CONNECT_BY_ISLEAF", "CONNECT_IDLE_TIME", "CONNECT_TIME", "CONSTANT", "CONSTRAINTS", "CONSTRUCTOR", "CONTEXT", "COPY", "CORRESPONDING", "COUNT", "COUNTER", "CPU_PER_CALL", "CPU_PER_SESSION", "CTLFILE", "CUMULATIVE", "CURRENT_SCHEMA", "CURRENT_USER", "CYCLE", "DANGLING", "DATA", "DATABASE", "DATAFILE", "DATE", "DATETIME", "DAY", "DBFILE", "DDL", "DDL_CLONE", "DEBUG", "DEC", "DEFERRABLE", "DEFERRED", "DEFINER", "DELETING", "DELIMITED", "DELTA", "DEMAND", "DENSE_RANK", "DEREF", "DETACH", "DETERMINISTIC", "DEVICE", "DIAGNOSTICS", "DICTIONARY", "DISCONNECT", "DOWN", "DUMP", "EACH", "ENCRYPT", "ENCRYPTION", " ERROR", "ERRORS", "ESCAPE", "EVENTINFO", "EVENTS", "EXCEPTION", "EXCEPTIONS", "EXCEPTION_INIT", "EXCLUDE", "EXCLUDING", "EXCLUSIVE", "EXTENDS", "EXTERNAL", "EXTERNALLY", "FAILED_LOGIN_ATTEMPS", "FAST", "FIELDS", "FILE", "FILEGROUP", "FILESIZE", "FILLFACTOR", "FINAL", "FOLLOWING", "FORALL", "FORCE", "FREQUENCE", "GLOBAL", "GLOBALLY", "HASH", "HEXTORAW", "HOLD", "HOUR", "HUGE", "IDENTIFIED", "IDENTITY_INSERT", "IMAGE", "IN", "INCLUDE", "INCLUDING", "INCREASE", "INCREMENT", "INDEXES", "INDICES", "INITIAL", "INITIALIZED", "INITIALLY", "INNERID", "INPUT", "INSENSITIVE", "INSERTING", "INSTANTIABLE", "INSTEAD", "INTEGER", "INTENT", "INVISIBLE", "ISOLATION", "JOB", "KEY", "LABEL", "LARGE", "LAST", "LESS", "LEVEL", "LEXER", "LIMIT", "LOB", "LOCAL", "LOCALLY", "LOCK", "LOCKED", "LOG", "LOGFILE", "LOGGING", "LOGIC", "LOGOFF", "LOGON", "LOGOUT", "LONG", "LONGVARBINARY", "LONGVARCHAR", "LSN", "MANUAL", "MAP", "MAPPED", "MATCH", "MATCHED", "MATERIALIZED", "MAX", "MAXPIECESIZE", "MAXSIZE", "MAXVALUE", "MEMORY", "MEM_SPACE", "MERGE", "MIN", "MINEXTENTS", "MINUTE", "MINVALUE", "MIRROR", "MOD", "MODE", "MODIFY", "MONEY", "MONITORING", "MONTH", "MOUNT", "MOVEMENT", "NATIONAL", "NCHAR", "NCHARACTER", "NEVER", "NO", "NOARCHIVELOG", "NOAUDIT", "NOBRANCH", "NOCACHE", "NOLOGGING", "NOMAXVALUE", "NOMINVALUE", "NOMONITORING", "NONE", "NOORDER", "NORMAL", "NOSORT", "NOT_ALLOW_DATETIME", "NOT_ALLOW_IP", "NOWAIT", "NULLS", "NUMBER", "NUMERIC", "OFF", "OFFLINE", "OFFSET", "OLD", "ONCE", "ONLINE", "ONLY", "OPEN", "OPTIMIZE", "OPTION", "OUTER", "OVERLAPS", "OVERRIDING", "PACKAGE", "PAD", "PAGE", "PARALLEL", "PARALLEL_ENABLE", "PARMS", "PARTIAL", "PARTITIONS", "PASSING", "PASSWORD_GRACE_TIME", "PASSWORD_LIFE_TIME", "PASSWORD_LOCK_TIME", "PASSWORD_POLICY", "PASSWORD_REUSE_MAX", "PASSWORD_REUSE_TIME", "PATH", "PIPE", "PIPELINED", "PIVOT", "PLACING", "PLS_INTEGER", "PRAGMA", "PRECEDING", "PRECISION", "PRESERVE", "PRIVILEGE", "PURGE", "QUERY_REWRITE_INTEGRITY", "RANGE", "RAWTOHEX", "READ", "READONLY", "READ_PER_CALL", "READ_PER_SESSION", "REAL", "REBUILD", "RECORD", "RECORDS", "REFRESH", "RELATED", "RENAME", "REPEATABLE", "REPLACE", "REPLAY", "RESIZE", "RESTORE", "RESTRICT", "RESULT", "RESULT_CACHE", "ROLE", "ROLLFILE", "ROOT", "ROWCOUNT", "ROWID", "RULE", "SALT", "SAMPLE", "SAVE", "SCOPE", "SCROLL", "SECOND", "SEED", "SELF", "SENSITIVE", "SEQUENCE", "SERERR", "SERIALIZABLE", "SERVER", "SESSION", "SESSION_PER_USER", "SHARE", "SHUTDOWN", "SIBLINGS", "SIMPLE", "SINCE", "SIZE", "SKIP", "SMALLINT", "SNAPSHOT", "SOUND", "SPACE", "SPATIAL", "SPFILE", "SPLIT", "SQL", "STANDBY", "STARTUP", "STAT", "STATEMENT", "STORAGE", "STORE", "STRING", "STYLE", "SUBPARTITION", "SUBPARTITIONS", "SUBSTRING", "SUBTYPE", "SUCCESSFUL", "SUM", "SUSPEND", "SYNC", "SYNCHRONOUS", "SYSTEM ", "SYS_CONNECT_BY_PATH", "TABLESPACE", "TASK", "TEMPLATE", "TEMPORARY", "TEXT", "THAN", "THEN", "THREAD", "TIES", "TIME", "TIMER", "TIMES", "TIMESTAMP", "TIME_ZONE", "TINYINT", "TRACE ", "TRANSACTION", "TRANSACTIONAL", "TRIGGERS", "TRUNCSIZE", "TRXID", "TYPE", "UNBOUNDED", "UNCOMMITTED", "UNDER", "UNLIMITED", "UNLOCK ", "UNPIVOT", "UNUSABLE ", "UP", "UPDATING", " USE_HASH ", "USE_MERGE ", "USE_NL ", "USE_NL_WITH_INDEX ", "VALUE", "VARBINARY", "VARCHAR", "VARCHAR2", "VARIANCE", "VARYING", "VERSIONS", "VERSIONS_STARTTIME", "VERSIONS_ENDTIME", "VERSIONS_STARTTRXID", "VERSIONS_ENDTRXID", "VERSIONS_OPERATION", "VERTICAL", "VISIBLE ", "VSIZE", "WAIT", "WEEK", "WITHOUT", "WORK", "WRAPPED", "WRITE", "XML", "XMLTABLE", "YEAR", "ZONE");
    private Integer databaseMajorVersion;
    private Integer databaseMinorVersion;
    
    public DM8Database() {
    }
    
    
    public int getDatabaseMajorVersion() throws DatabaseException {
        return super.getDatabaseMajorVersion();
    }
    public int getDatabaseMinorVersion() throws DatabaseException {
        return super.getDatabaseMinorVersion();
    }
    //加入关键字
    protected boolean mustQuoteObjectName(String objectName, Class<? extends DatabaseObject> objectType) {
        return QUOTE_KEYWORDS.contains(objectName.toUpperCase());
    }
    @Override
    public String getShortName() {
        return PRODUCT_SHORT_NAME;
    }
    
    @Override
    protected String getDefaultDatabaseProductName() {
        return getDatabaseProductName();
    }
    
    @Override
    public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {
        return PRODUCT_NAME.equalsIgnoreCase(conn.getDatabaseProductName());
    }
    
    
    @Override
    public String getDefaultDriver(String url) {
        
        return url.startsWith(DRIVER_CONN_PERFIX) ? DRIVER_STRING : null;
    }
    
    @Override
    public Integer getDefaultPort() {
        return 5236;
    }
    
    
}

在这里插入图片描述

2. 部分雾化试图不兼容重写fastFetchQuery

liquibase.snapshot.ResultSetCache.SingleResultSetExtractor#fastFetchQuery
一定要放到oracle判断之前
在这里插入图片描述

                if (database instanceof DM8Database) {
                        return queryDameng(catalogAndSchema, table);
                    }else if (database instanceof OracleDatabase) {
                        return queryOracle(catalogAndSchema, table);
                    } else if (database instanceof MSSQLDatabase) {
                        return queryMssql(catalogAndSchema, table);
                    } else if (database instanceof Db2zDatabase) {
                        return queryDb2Zos(catalogAndSchema, table);
                    } 

3. 部分雾化试图不兼容重写bulkFetchQuery

liquibase.snapshot.ResultSetCache.SingleResultSetExtractor#bulkFetchQuery
instanceof类型比较的原因所以判断一定要放到oracle判断之前。
在这里插入图片描述

                    if(database instanceof DM8Database) {
                        return queryDameng(catalogAndSchema, null);
                    }else if (database instanceof OracleDatabase) {
                        return queryOracle(catalogAndSchema, null);
                    } else if (database instanceof MSSQLDatabase) {
                        return queryMssql(catalogAndSchema, null);
                    } else if (database instanceof Db2zDatabase) {
                        return queryDb2Zos(catalogAndSchema, null);
                    }

4. 新增queryDameng方法

liquibase.snapshot.ResultSetCache.SingleResultSetExtractor#queryDameng

                private List<CachedRow> queryDameng(CatalogAndSchema catalogAndSchema, String viewName) throws DatabaseException, SQLException {
                    String ownerName = CachingDatabaseMetaData.this.database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class);
                    String sql = "SELECT null as TABLE_CAT, a.OWNER as TABLE_SCHEM, a.VIEW_NAME as TABLE_NAME, 'TABLE' as TABLE_TYPE, c.COMMENTS as REMARKS, TEXT as OBJECT_BODY";
                    if (CachingDatabaseMetaData.this.database.getDatabaseMajorVersion() > 10) {
                        sql = sql + ", EDITIONING_VIEW";
                    }

                    sql = sql + " from ALL_VIEWS a join ALL_TAB_COMMENTS c on a.VIEW_NAME=c.table_name and a.owner=c.owner ";
                    if (viewName == null && JdbcDatabaseSnapshot.this.getAllCatalogsStringScratchData() != null) {
                        sql = sql + "WHERE a.OWNER IN ('" + ownerName + "', " + JdbcDatabaseSnapshot.this.getAllCatalogsStringScratchData() + ")";
                    } else {
                        sql = sql + "WHERE a.OWNER='" + ownerName + "'";
                    }

                    if (viewName != null) {
                        sql = sql + " AND a.VIEW_NAME='" + CachingDatabaseMetaData.this.database.correctObjectName(viewName, View.class) + "'";
                    }

                    return this.executeAndExtract(sql, CachingDatabaseMetaData.this.database);
                }

5. 修正源码的test方法

liquibase.change.ChangeParameterMetaDataTest#computedDatabasesCorrect
在这里插入图片描述

6. 修改版本号

maven打包的话,只修改liquibase-core的pom的版本号,然后打包即可。

在这里插入图片描述

已经提交打包了一份:点击跳转liquibase-core-3.8.10-local-SNAPSHOT.jar

四、修改后的校验

项目中的校验

gitee项目地址:https://gitee.com/gy297879328/jpa_dm_liquibase
Demo已经搭建完毕,启动项目后,数据库中的日志会初始化liquibase两张表跟master.xml的表
在这里插入图片描述

数据库中的校验

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值