信创改造<flowable适配国产达梦数据库方案>

1,162 阅读1分钟

为什么国产化

  1. 国产化是指技术引进项目投产后所生产的产品中,国内生产件的数量占整件产品生产件数量。换句话说,软件国产化的占比,直接影响到技术是否会在某一个时点上被”卡脖子“。

Flowable国产化方案

  1. Flowable默认支持数据库有: MySQL,Oracle,PostgreSQL,SQLServer,DB2等,但是这些数据库都是国外所研发的,要想符合信创要求则需让flowable支持国产数据库,我这里选用的达梦数据库

  2. 为什么选用达梦数据库

    达梦数据库借鉴了 Oracle 数据库的设计思想,其兼容性比较强,有专门提供可视化的数据迁移工具,易于学习和使用,具备高性能,灵活性,可靠性,支持多种操作系统等优点

改造方式(这里使用新创建项目进行改造,如老项目迁移,请使用达梦的迁移工具进行迁移)

  1. 创建boot项目

  2. 引入dm驱动,flowable驱动完整maven如下:

  3. <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
        <modelVersion4.0.0</modelVersion
        <parent
            <groupIdorg.springframework.boot</groupId
            <artifactIdspring-boot-starter-parent</artifactId
            <version2.6.11</version
            <relativePath/> <!-- lookup parent from repository -->
        </parent
        <groupIdcom.gx</groupId
        <artifactIdflowable-dm-demo</artifactId
        <version0.0.1-SNAPSHOT</version
        <namedm-demo</name
        <descriptiondm-demo</description
        <packagingjar</packaging
        <properties
            <java.version1.8</java.version
            <knife4j.version3.0.3</knife4j.version
            <mybatis-plus.version3.5.2</mybatis-plus.version
            <hutool-all.version5.8.15</hutool-all.version
            <druid.version1.2.16</druid.version
            <knife4j.version3.0.3</knife4j.version
        </properties
        <dependencies
            <dependency
                <groupIdorg.flowable</groupId
                <artifactIdflowable-spring-boot-starter-basic</artifactId
                <version6.7.2</version
            </dependency
            <!--api 文档-->
            <dependency
                <groupIdcom.github.xiaoymin</groupId
                <artifactIdknife4j-spring-boot-starter</artifactId
                <version${knife4j.version}</version
            </dependency
    
            <dependency
                <groupIdorg.springframework.boot</groupId
                <artifactIdspring-boot-starter-web</artifactId
            </dependency
            <!--导入dm的驱动版本-->
            <dependency
                <groupIdcom.dameng</groupId
                <artifactIdDmJdbcDriver18</artifactId
                <version8.1.2.192</version
            </dependency
            <!--添加 druid  starter-->
            <dependency
                <groupIdcom.alibaba</groupId
                <artifactIddruid-spring-boot-starter</artifactId
                <version${druid.version}</version
            </dependency
            <dependency
                <groupIdcom.baomidou</groupId
                <artifactIdmybatis-plus-boot-starter</artifactId
                <version${mybatis-plus.version}</version
            </dependency
            <dependency
                <groupIdorg.projectlombok</groupId
                <artifactIdlombok</artifactId
                <optionaltrue</optional
            </dependency
            <!--工具类包-->
            <dependency
                <groupIdcn.hutool</groupId
                <artifactIdhutool-all</artifactId
                <version${hutool-all.version}</version
            </dependency
            <dependency
                <groupIdorg.springframework.boot</groupId
                <artifactIdspring-boot-starter-test</artifactId
                <scopetest</scope
            </dependency
        </dependencies
    
        <build
            <plugins
                <plugin
                    <groupIdorg.apache.maven.plugins</groupId
                    <artifactIdmaven-war-plugin</artifactId
                </plugin
            </plugins
        </build
    
    </project
    
  4. yml配置数据库连接,核心在达梦数据库连接后新增?compatibleMode=oracle配置,注意连接上不用配置库名,会根据用户去进行库操作,达梦数据库创建步骤:新增表空间(此步骤可跳过)->新增用户并分配权限角色->自动生成模式 application.yml

    server:
      port: 9160
    spring:
      mvc:
        path match:
          matching-strategy: ant_path_matcher
      main:
        allow-circular-references: true
      application:
        name: flowable-dm
      profiles:
        active: dev
      servlet:
        multipart:
          max-file-size: 20MB
          max-request-size: 100MB
      session:
        store-type: redis
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
        default-property-inclusion: non_null
    mybatis-plus:
      mapper-locations: classpath:/mapper/**/*.xml
      global-config:
        db-config:
          id-type: auto
          logic-delete-value: 1
          logic-not-delete-value: 0
          schema: FLOWABLE_DM
        banner: on
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    flowable:
      async-executor-activate: false #定时任务JOB
      #  将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
      database-schema-update: true
    

    application-dev.yml

    spring:
      datasource:
        #数据源类型
        type: com.alibaba.druid.pool.DruidDataSource
        username: FLOWABLE_DM
        password: FLOWABLE_DM
        url: jdbc:dm://ip:port?compatibleMode=oracle
        driver-class-name: dm.jdbc.driver.DmDriver
        druid:
          initial-size: 5
          min-idle: 5
          max-active: 25
          max-wait: 6000
          test-while-idle: false
    
  5. 配置flowable(老项目应该已经配置过了,此处为新引入flowable初始化配置): 完整代码:

    package com.gx.common.config.flowable;
    
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.flowable.spring.SpringProcessEngineConfiguration;
    import org.flowable.spring.boot.EngineConfigurationConfigurer;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @Author Liu-pj
     * @Date 2021/12/21 18:50
     * @Desc 流程字体配置
     **/
    @Configuration
    public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
        @Override
        public void configure(SpringProcessEngineConfiguration engineConfiguration) {
            engineConfiguration.setActivityFontName("宋体");
            engineConfiguration.setLabelFontName("宋体");
            engineConfiguration.setAnnotationFontName("宋体");
        }
    }
    
    
    
    
  6. 直接启动,发现达梦数据库flowable表已经成功创建,但是有个报错 image-20240104182305377.png

  7. 解决此报错,覆盖源码方式,注意包名必须一致 image-20240104182501053.png

完整代码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package liquibase.database.core;

import dm.jdbc.driver.DmdbConnection;
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.statement.DatabaseFunction;
import liquibase.statement.SequenceCurrentValueFunction;
import liquibase.statement.SequenceNextValueFunction;
import liquibase.statement.UniqueConstraint;
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.StringUtil;

import java.lang.reflect.Method;
import java.sql.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class OracleDatabase extends AbstractJdbcDatabase {
    public static final Pattern PROXY_USER = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*");
    public static final String PRODUCT_NAME = "oracle";
    private static final ResourceBundle coreBundle = ResourceBundle.getBundle("liquibase/i18n/liquibase-core");
    protected final int SHORT_IDENTIFIERS_LENGTH = 30;
    protected final int LONG_IDENTIFIERS_LEGNTH = 128;
    public static final int ORACLE_12C_MAJOR_VERSION = 12;
    private final Set<String> reservedWords = new HashSet();
    private Set<String> userDefinedTypes;
    private Map<String, String> savedSessionNlsSettings;
    private Boolean canAccessDbaRecycleBin;
    private Integer databaseMajorVersion;
    private Integer databaseMinorVersion;

    public OracleDatabase() {
        super.unquotedObjectsAreUppercased = true;
        super.setCurrentDateTimeFunction("SYSTIMESTAMP");
        this.dateFunctions.add(new DatabaseFunction("SYSDATE"));
        this.dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP"));
        this.dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP"));
        super.sequenceNextValueFunction = "%s.nextval";
        super.sequenceCurrentValueFunction = "%s.currval";
    }

    public int getPriority() {
        return 1;
    }

    private void tryProxySession(String url, Connection con) {
        Matcher m = PROXY_USER.matcher(url);
        if (m.matches()) {
            Properties props = new Properties();
            props.put("PROXY_USER_NAME", m.group(1));
            Method method;
            try {
                method = con.getClass().getMethod("openProxySession", Integer.TYPE, Properties.class);
                method.setAccessible(true);
                method.invoke(con, 1, props);
            } catch (Exception var8) {
                Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + var8.getCause().getMessage());
                return;
            }

            try {
                method = con.getClass().getMethod("isProxySession");
                method.setAccessible(true);
                boolean b = (Boolean) method.invoke(con);
                if (!b) {
                    Scope.getCurrentScope().getLog(this.getClass()).info("Proxy session not established on OracleDatabase: ");
                }
            } catch (Exception var7) {
                Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + var7.getCause().getMessage());
            }
        }

    }

    public void setConnection(DatabaseConnection conn) {
        this.reservedWords.addAll(Arrays.asList("GROUP", "USER", "SESSION", "PASSWORD", "RESOURCE", "START", "SIZE", "UID", "DESC", "ORDER"));
        Connection sqlConn = null;
        if (!(conn instanceof OfflineConnection)) {
            try {
                if (conn instanceof JdbcConnection) {
                    sqlConn = ((JdbcConnection) conn).getWrappedConnection();
                }
            } catch (Exception var16) {
                throw new UnexpectedLiquibaseException(var16);
            }

            if (sqlConn != null) {
                this.tryProxySession(conn.getURL(), sqlConn);

                try {
                    this.reservedWords.addAll(Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*")));
                } catch (SQLException var15) {
                    Scope.getCurrentScope().getLog(this.getClass()).info("Could get sql keywords on OracleDatabase: " + var15.getMessage());
                }

                try {
                    Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE);
                    method.setAccessible(true);
                    method.invoke(sqlConn, true);
                } catch (Exception var14) {
                    Scope.getCurrentScope().getLog(this.getClass()).info("Could not set remarks reporting on OracleDatabase: " + var14.getMessage());
                }

                CallableStatement statement = null;

                try {
                    DatabaseMetaData metaData = sqlConn.getMetaData();
                    Connection connection = metaData.getConnection();
                    if (connection instanceof DmdbConnection) {
                        String compatibleVersion = "11.2.0.4.0";
                        Matcher majorVersionMatcher = Pattern.compile("(\\d+)\\.(\\d+)\\..*").matcher(compatibleVersion);
                        if (majorVersionMatcher.matches()) {
                            this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1));
                            this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2));
                        }
                    } else {
                        statement = sqlConn.prepareCall("{call DBMS_UTILITY.DB_VERSION(?,?)}");
                        statement.registerOutParameter(1, 12);
                        statement.registerOutParameter(2, 12);
                        statement.execute();
                        String compatibleVersion = statement.getString(2);
                        if (compatibleVersion != null) {
                            Matcher majorVersionMatcher = Pattern.compile("(\\d+)\\.(\\d+)\\..*").matcher(compatibleVersion);
                            if (majorVersionMatcher.matches()) {
                                this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1));
                                this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2));
                            }
                        }
                    }
                } catch (SQLException var12) {
                    String message = "Cannot read from DBMS_UTILITY.DB_VERSION: " + var12.getMessage();
                    Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + message);
                } finally {
                    JdbcUtils.closeStatement(statement);
                }
            }
        }

        super.setConnection(conn);
    }

    public String getShortName() {
        return "oracle";
    }

    protected String getDefaultDatabaseProductName() {
        return "Oracle";
    }

    public int getDatabaseMajorVersion() throws DatabaseException {
        return this.databaseMajorVersion == null ? super.getDatabaseMajorVersion() : this.databaseMajorVersion;
    }

    public int getDatabaseMinorVersion() throws DatabaseException {
        return this.databaseMinorVersion == null ? super.getDatabaseMinorVersion() : this.databaseMinorVersion;
    }

    public Integer getDefaultPort() {
        return 1521;
    }

    public String getJdbcCatalogName(CatalogAndSchema schema) {
        return null;
    }

    public String getJdbcSchemaName(CatalogAndSchema schema) {
        return this.correctObjectName(schema.getCatalogName() == null ? schema.getSchemaName() : schema.getCatalogName(), Schema.class);
    }

    protected String getAutoIncrementClause(String generationType, Boolean defaultOnNull) {
        if (StringUtil.isEmpty(generationType)) {
            return super.getAutoIncrementClause();
        } else {
            String autoIncrementClause = "GENERATED %s AS IDENTITY";
            String generationStrategy = generationType;
            if (Boolean.TRUE.equals(defaultOnNull) && generationType.equalsIgnoreCase("BY DEFAULT")) {
                generationStrategy = generationType + " ON NULL";
            }

            return String.format(autoIncrementClause, generationStrategy);
        }
    }

    public String generatePrimaryKeyName(String tableName) {
        return tableName.length() > 27 ? "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27) : "PK_" + tableName.toUpperCase(Locale.US);
    }

    public boolean supportsInitiallyDeferrableColumns() {
        return true;
    }

    public boolean isReservedWord(String objectName) {
        return this.reservedWords.contains(objectName.toUpperCase());
    }

    public boolean supportsSequences() {
        return true;
    }

    public boolean supportsSchemas() {
        return false;
    }

    protected String getConnectionCatalogName() throws DatabaseException {
        if (this.getConnection() instanceof OfflineConnection) {
            return this.getConnection().getCatalog();
        } else if (!(this.getConnection() instanceof JdbcConnection)) {
            return this.defaultCatalogName;
        } else {
            try {
                return Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class);
            } catch (Exception var2) {
                Scope.getCurrentScope().getLog(this.getClass()).info("Error getting default schema", var2);
                return null;
            }
        }
    }

    public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {
        return "oracle".equalsIgnoreCase(conn.getDatabaseProductName());
    }

    public String getDefaultDriver(String url) {
        return url.startsWith("jdbc:oracle") ? "oracle.jdbc.OracleDriver" : null;
    }

    public String getDefaultCatalogName() {
        return super.getDefaultCatalogName() == null ? null : super.getDefaultCatalogName().toUpperCase(Locale.US);
    }

    public String getDateLiteral(String isoDate) {
        String normalLiteral = super.getDateLiteral(isoDate);
        if (this.isDateOnly(isoDate)) {
            return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')";
        } else if (this.isTimeOnly(isoDate)) {
            return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')";
        } else if (this.isTimestamp(isoDate)) {
            return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')";
        } else if (this.isDateTime(isoDate)) {
            int seppos = normalLiteral.lastIndexOf(46);
            if (seppos != -1) {
                normalLiteral = normalLiteral.substring(0, seppos) + "'";
            }

            return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')";
        } else {
            return "UNSUPPORTED:" + isoDate;
        }
    }

    public boolean isSystemObject(DatabaseObject example) {
        if (example == null) {
            return false;
        } else if (this.isLiquibaseObject(example)) {
            return false;
        } else {
            if (example instanceof Schema) {
                label131:
                {
                    if (!"SYSTEM".equals(example.getName()) && !"SYS".equals(example.getName()) && !"CTXSYS".equals(example.getName()) && !"XDB".equals(example.getName())) {
                        if (!"SYSTEM".equals(example.getSchema().getCatalogName()) && !"SYS".equals(example.getSchema().getCatalogName()) && !"CTXSYS".equals(example.getSchema().getCatalogName()) && !"XDB".equals(example.getSchema().getCatalogName())) {
                            break label131;
                        }

                        return true;
                    }

                    return true;
                }
            } else if (this.isSystemObject(example.getSchema())) {
                return true;
            }

            if (example instanceof Catalog) {
                if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) {
                    return true;
                }
            } else if (example.getName() != null) {
                if (example.getName().startsWith("BIN$")) {
                    boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin();
                    if (!filteredInOriginalQuery) {
                        filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName());
                    }

                    if (!filteredInOriginalQuery) {
                        return true;
                    }

                    return !(example instanceof PrimaryKey) && !(example instanceof Index) && !(example instanceof UniqueConstraint);
                }

                if (example.getName().startsWith("AQ$")) {
                    return true;
                }

                if (example.getName().startsWith("DR$")) {
                    return true;
                }

                if (example.getName().startsWith("SYS_IOT_OVER")) {
                    return true;
                }

                if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) {
                    return true;
                }

                if (example.getName().startsWith("MLOG$_")) {
                    return true;
                }

                if (example.getName().startsWith("RUPD$_")) {
                    return true;
                }

                if (example.getName().startsWith("WM$_")) {
                    return true;
                }

                if ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) {
                    return true;
                }

                if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) {
                    return true;
                }

                if (example.getName().startsWith("ISEQ$$_")) {
                    return true;
                }

                if (example.getName().startsWith("USLOG$")) {
                    return true;
                }

                if (example.getName().startsWith("SYS_FBA")) {
                    return true;
                }
            }

            return super.isSystemObject(example);
        }
    }

    public boolean supportsTablespaces() {
        return true;
    }

    public boolean supportsAutoIncrement() {
        boolean isAutoIncrementSupported = false;

        try {
            if (this.getDatabaseMajorVersion() >= 12) {
                isAutoIncrementSupported = true;
            }
        } catch (DatabaseException var3) {
            isAutoIncrementSupported = false;
        }

        return isAutoIncrementSupported;
    }

    public boolean supportsRestrictForeignKeys() {
        return false;
    }

    public int getDataTypeMaxParameters(String dataTypeName) {
        if ("BINARY_FLOAT".equalsIgnoreCase(dataTypeName)) {
            return 0;
        } else {
            return "BINARY_DOUBLE".equalsIgnoreCase(dataTypeName) ? 0 : super.getDataTypeMaxParameters(dataTypeName);
        }
    }

    public String getSystemTableWhereClause(String tableNameColumn) {
        List<String> clauses = new ArrayList(Arrays.asList("BIN$", "AQ$", "DR$", "SYS_IOT_OVER", "MLOG$_", "RUPD$_", "WM$_", "ISEQ$$_", "USLOG$", "SYS_FBA"));

        for (int i = 0; i < clauses.size(); ++i) {
            clauses.set(i, tableNameColumn + " NOT LIKE '" + clauses.get(i) + "%'");
        }

        return "(" + StringUtil.join(clauses, " AND ") + ")";
    }

    public boolean jdbcCallsCatalogsSchemas() {
        return true;
    }

    public Set<String> getUserDefinedTypes() {
        if (this.userDefinedTypes == null) {
            this.userDefinedTypes = new HashSet();
            if (this.getConnection() != null && !(this.getConnection() instanceof OfflineConnection)) {
                try {
                    try {
                        this.userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class));
                    } catch (DatabaseException var2) {
                        this.userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class));
                    }
                } catch (DatabaseException var3) {
                }
            }
        }

        return this.userDefinedTypes;
    }

    public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) {
        if (databaseFunction != null && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) {
            return databaseFunction.toString();
        } else if (!(databaseFunction instanceof SequenceNextValueFunction) && !(databaseFunction instanceof SequenceCurrentValueFunction)) {
            return super.generateDatabaseFunctionValue(databaseFunction);
        } else {
            String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction);
            return quotedSeq.replaceFirst("\"([^\\.\"]+)\\.([^\\.\"]+)\"", "\"$1\".\"$2\"");
        }
    }

    public ValidationErrors validate() {
        ValidationErrors errors = super.validate();
        DatabaseConnection connection = this.getConnection();
        if (connection != null && !(connection instanceof OfflineConnection)) {
            if (!this.canAccessDbaRecycleBin()) {
                errors.addWarning(this.getDbaRecycleBinWarning());
            }

            return errors;
        } else {
            Scope.getCurrentScope().getLog(this.getClass()).info("Cannot validate offline database");
            return errors;
        }
    }

    public String getDbaRecycleBinWarning() {
        return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where constraints are deleted and restored. Since Oracle doesn't properly restore the original table names referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this issue.\n\nThe user you used to connect to the database (" + this.getConnection().getConnectionUserName() + ") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. Please run the following SQL to set the appropriate permissions, and try running the command again.\n\n     GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + this.getConnection().getConnectionUserName() + ";";
    }

    public boolean canAccessDbaRecycleBin() {
        if (this.canAccessDbaRecycleBin == null) {
            DatabaseConnection connection = this.getConnection();
            if (connection == null || connection instanceof OfflineConnection) {
                return false;
            }

            Statement statement = null;

            try {
                statement = ((JdbcConnection) connection).createStatement();
                ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1");
                resultSet.close();
                this.canAccessDbaRecycleBin = true;
            } catch (Exception var7) {
                if (var7 instanceof SQLException && var7.getMessage().startsWith("ORA-00942")) {
                    this.canAccessDbaRecycleBin = false;
                } else {
                    Scope.getCurrentScope().getLog(this.getClass()).warning("Cannot check dba_recyclebin access", var7);
                    this.canAccessDbaRecycleBin = false;
                }
            } finally {
                JdbcUtils.close(null, statement);
            }
        }

        return this.canAccessDbaRecycleBin;
    }

    public boolean supportsNotNullConstraintNames() {
        return true;
    }

    public boolean isValidOracleIdentifier(String identifier, Class<? extends DatabaseObject> type) {
        if (identifier != null && identifier.length() >= 1) {
            if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$")) {
                return false;
            } else {
                return identifier.length() <= 128;
            }
        } else {
            return false;
        }
    }

    public int getIdentifierMaximumLength() {
        try {
            if (this.getDatabaseMajorVersion() < 12) {
                return 30;
            } else {
                return this.getDatabaseMajorVersion() == 12 && this.getDatabaseMinorVersion() <= 1 ? 30 : 128;
            }
        } catch (DatabaseException var2) {
            throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", var2);
        }
    }
}
  1. 编写流程相关操作接口

image-20240104182634727.png

  1. 测试接口,功能正常

image-20240104182805539.png