Flowable集成达梦8数据库

相信大部分人都和我一样,以为只要换个数据源就可以了,起初我只更换了数据源,并开启了自动创建表功能。

spring:datasource:url: jdbc:dm://127.0.0.1:5236?SCHEMA=FLOWABLEusername: FLOWABLEpassword: 123456driver-class-name: dm.jdbc.driver.DmDrivertype: com.alibaba.druid.pool.DruidDataSourceflowable:#关闭定时任务JOBasync-executor-activate: falsedatabase-schema-update: trueserver:port: 8081

但是运行时控制台报错

Caused by: org.flowable.common.engine.api.FlowableException: couldn’t deduct database type from database product name ‘DM DBMS’

根据Flowable官方文档,Flowable支持多种数据库,包括H2、Hsql、MySQL、Oracle、PostgreSQL、Microsoft SQL Server等,然而,根据Flowable官方文档,目前并不支持达梦数据库。

解决方案:

参考:https://blog.csdn.net/qq_37829708/article/details/124978212?spm=1001.2014.3001.5506
前排提示:如果按照步骤操作无解,可以查看我这篇博客的总结,里面也许会有解决方案。

在pom.xml中排除liquibase依赖,指定一个版本防止后续更新,我测试了两个版本4.3.5和4.9.1,启动时创建表正常执行,只要是4.x的版本应该都是通用的。

<dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.8.0</version><exclusions><exclusion><groupId>org.liquibase</groupId><artifactId>liquibase-core</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.liquibase</groupId><artifactId>liquibase-core</artifactId><version>4.9.1</version></dependency>

修改Flowable源码

  1. 修改org.flowable.common.engine.impl.AbstractEngineConfiguration类,源码的位置如下:

    这里采用覆盖的方式重写该源码类,这样避免了重新打包的问题,在项目中创建org.flowable.common.engine.impl.AbstractEngineConfiguration类,将源码中的代码全部复制到该类中。

    创建后修改getDefaultDatabaseTypeMappings方法
public static Properties getDefaultDatabaseTypeMappings() {Properties databaseTypeMappings = new Properties();databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2);databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL);databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL);databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL);databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE);databaseTypeMappings.setProperty(PRODUCT_NAME_POSTGRES, DATABASE_TYPE_POSTGRES);databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL);databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/LINUXPPC64LE", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2);databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB);databaseTypeMappings.setProperty("DM DBMS", DATABASE_TYPE_ORACLE); // 新增内容return databaseTypeMappings;}
  1. 低版本Flowable没有dmn相关表的不用执行该步骤的操作,由于我是6.8版本,新增了许多其他功能,在启动时创建dmn相关表时,其中有一个更改表名的步骤,这个语法是达梦不支持的:

    解决方法:
    resource目录下新增org/flowable/dmn/db/liquibase/custom-flowable-dmn-db-changelog.xml文件,并把源码中的内容复制进去,将id为7的changeSet标签删除,然后把下面的内容赋值到对应位置中:
<changeSet id="7" author="flowable" dbms="!dm"><dropIndex tableName="ACT_DMN_DECISION_TABLE" indexName="ACT_IDX_DEC_TBL_UNIQ"/><renameTablenewTableName="ACT_DMN_DECISION"oldTableName="ACT_DMN_DECISION_TABLE"/><createIndex tableName="ACT_DMN_DECISION" indexName="ACT_IDX_DMN_DEC_UNIQ" unique="true"><column name="KEY_" /><column name="VERSION_" /><column name="TENANT_ID_" /></createIndex></changeSet><changeSet id="7" author="flowable" dbms="dm"><dropIndex tableName="ACT_DMN_DECISION_TABLE" indexName="ACT_IDX_DEC_TBL_UNIQ"/><sql>CREATE TABLE ACT_DMN_DECISION AS SELECT * FROM ACT_DMN_DECISION_TABLE;</sql><sql>DROP TABLE ACT_DMN_DECISION_TABLE;</sql><createIndex tableName="ACT_DMN_DECISION" indexName="ACT_IDX_DMN_DEC_UNIQ" unique="true"><column name="KEY_" /><column name="VERSION_" /><column name="TENANT_ID_" /></createIndex></changeSet>

java文件下创建DmnDbSchemaManager类,全路径为org.flowable.dmn.engine.impl.db.DmnDbSchemaManager,将源码复制到自己创建的类中,并更改LIQUIBASE_CHANGELOG绑定的路径,更改为上面创建的custom-flowable-dmn-db-changelog.xml文件的路径。

上述变更的源码所在的位置,复制对应源码到自己创建的文件中。

修改Liquibase源码

  1. java文件夹下创建DmDatabase类,全路径为liquibase.database.core.DmDatabase,参考了liquibase.database.core.OracleDatabase类,将下面代码直接粘进新创建的类。

    在这个类中由作者介绍更改如下:

    1. 删除setConnection方法;
    2. 修改PRODUCT_NAME常量值为“DM DBMS”;
    3. 修改getDefaultPort方法,返回5236;
    4. 修改getShortName方法,返回dm;
    5. 修改getDefaultDriver方法,返回达梦的Driver;
package liquibase.database.core;import java.lang.reflect.Method;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.ArrayList;import java.util.Arrays;import java.util.HashSet;import java.util.List;import java.util.Locale;import java.util.Map;import java.util.Properties;import java.util.Set;import java.util.regex.Matcher;import java.util.regex.Pattern;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.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;public class DmDatabase extends AbstractJdbcDatabase {private static final String PRODUCT_NAME = "DM DBMS";@Overrideprotected String getDefaultDatabaseProductName() {return PRODUCT_NAME;}/** * Is this AbstractDatabase subclass the correct one to use for the given connection. * * @param conn */@Overridepublic boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {return PRODUCT_NAME.equalsIgnoreCase(conn.getDatabaseProductName());}/** * If this database understands the given url, return the default driver class name.Otherwise return null. * * @param url */@Overridepublic String getDefaultDriver(String url) {if(url.startsWith("jdbc:dm")) {return "dm.jdbc.driver.DmDriver";}return null;}/** * Returns an all-lower-case short name of the product.Used for end-user selecting of database type * such as the DBMS precondition. */@Overridepublic String getShortName() {return "dm";}@Overridepublic Integer getDefaultPort() {return 5236;}/** * Returns whether this database support initially deferrable columns. */@Overridepublic boolean supportsInitiallyDeferrableColumns() {return true;}@Overridepublic boolean supportsTablespaces() {return true;}@Overridepublic int getPriority() {return PRIORITY_DEFAULT;}private static final Pattern PROXY_USER = Pattern.compile(".*(" />);protected final int SHORT_IDENTIFIERS_LENGTH = 30;protected final int LONG_IDENTIFIERS_LEGNTH = 128;public static final int ORACLE_12C_MAJOR_VERSION = 12;private Set<String> reservedWords = new HashSet<>();private Set<String> userDefinedTypes;private Map<String, String> savedSessionNlsSettings;private Boolean canAccessDbaRecycleBin;private Integer databaseMajorVersion;private Integer databaseMinorVersion;/** * Default constructor for an object that represents the Oracle Database DBMS. */public DmDatabase() {super.unquotedObjectsAreUppercased = true;//noinspection HardCodedStringLiteralsuper.setCurrentDateTimeFunction("SYSTIMESTAMP");// Setting list of Oracle's native functions//noinspection HardCodedStringLiteraldateFunctions.add(new DatabaseFunction("SYSDATE"));//noinspection HardCodedStringLiteraldateFunctions.add(new DatabaseFunction("SYSTIMESTAMP"));//noinspection HardCodedStringLiteraldateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP"));//noinspection HardCodedStringLiteralsuper.sequenceNextValueFunction = "%s.nextval";//noinspection HardCodedStringLiteralsuper.sequenceCurrentValueFunction = "%s.currval";}private void tryProxySession(final String url, final Connection con) {Matcher m = PROXY_USER.matcher(url);if (m.matches()) {Properties props = new Properties();props.put("PROXY_USER_NAME", m.group(1));try {Method method = con.getClass().getMethod("openProxySession", int.class, Properties.class);method.setAccessible(true);method.invoke(con, 1, props);} catch (Exception e) {Scope.getCurrentScope().getLog(getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage());}}}@Overridepublic int getDatabaseMajorVersion() throws DatabaseException {if (databaseMajorVersion == null) {return super.getDatabaseMajorVersion();} else {return databaseMajorVersion;}}@Overridepublic int getDatabaseMinorVersion() throws DatabaseException {if (databaseMinorVersion == null) {return super.getDatabaseMinorVersion();} else {return databaseMinorVersion;}}@Overridepublic String getJdbcCatalogName(CatalogAndSchema schema) {return null;}@Overridepublic String getJdbcSchemaName(CatalogAndSchema schema) {return correctObjectName((schema.getCatalogName() == null) ? schema.getSchemaName() : schema.getCatalogName(), Schema.class);}@Overrideprotected String getAutoIncrementClause(final String generationType, final Boolean defaultOnNull) {if (StringUtil.isEmpty(generationType)) {return super.getAutoIncrementClause();}String autoIncrementClause = "GENERATED %s AS IDENTITY"; // %s -- [ ALWAYS | BY DEFAULT [ ON NULL ] ]String generationStrategy = generationType;if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) {generationStrategy += " ON NULL";}return String.format(autoIncrementClause, generationStrategy);}@Overridepublic String generatePrimaryKeyName(String tableName) {if (tableName.length() > 27) {//noinspection HardCodedStringLiteralreturn "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27);} else {//noinspection HardCodedStringLiteralreturn "PK_" + tableName.toUpperCase(Locale.US);}}@Overridepublic boolean isReservedWord(String objectName) {return reservedWords.contains(objectName.toUpperCase());}@Overridepublic boolean supportsSequences() {return true;}/** * Oracle supports catalogs in liquibase terms * * @return false */@Overridepublic boolean supportsSchemas() {return false;}@Overrideprotected String getConnectionCatalogName() throws DatabaseException {if (getConnection() instanceof OfflineConnection) {return getConnection().getCatalog();}try {//noinspection HardCodedStringLiteralreturn Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class);} catch (Exception e) {//noinspection HardCodedStringLiteralScope.getCurrentScope().getLog(getClass()).info("Error getting default schema", e);}return null;}@Overridepublic String getDefaultCatalogName() {//NOPMDreturn (super.getDefaultCatalogName() == null) ? null : super.getDefaultCatalogName().toUpperCase(Locale.US);}/** * 

Returns an Oracle date literal with the same value as a string formatted using ISO 8601.

* *

Convert an ISO8601 date string to one of the following results: * to_date('1995-05-23', 'YYYY-MM-DD') * to_date('1995-05-23 09:23:59', 'YYYY-MM-DD HH24:MI:SS')

*

* Implementation restriction:
* Currently, only the following subsets of ISO8601 are supported:
*

    *
  • YYYY-MM-DD
  • *
  • YYYY-MM-DDThh:mm:ss
  • *
*/
@Overridepublic String getDateLiteral(String isoDate) {String normalLiteral = super.getDateLiteral(isoDate);if (isDateOnly(isoDate)) {return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')";} else if (isTimeOnly(isoDate)) {return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')";} else if (isTimestamp(isoDate)) {return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')";} else if (isDateTime(isoDate)) {int seppos = normalLiteral.lastIndexOf('.');if (seppos != -1) {normalLiteral = normalLiteral.substring(0, seppos) + "'";}return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')";}return "UNSUPPORTED:" + isoDate;}@Overridepublic boolean isSystemObject(DatabaseObject example) {if (example == null) {return false;}if (this.isLiquibaseObject(example)) {return false;}if (example instanceof Schema) {//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteralif ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) {return true;}//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteralif ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) {return true;}} else if (isSystemObject(example.getSchema())) {return true;}if (example instanceof Catalog) {//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteralif (("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName()))) {return true;}} else if (example.getName() != null) {//noinspection HardCodedStringLiteralif (example.getName().startsWith("BIN$")) { //oracle deleted tableboolean filteredInOriginalQuery = this.canAccessDbaRecycleBin();if (!filteredInOriginalQuery) {filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName());}if (filteredInOriginalQuery) {return !((example instanceof PrimaryKey) || (example instanceof Index) || (example instanceofliquibase.statement.UniqueConstraint));} else {return true;}} else //noinspection HardCodedStringLiteralif (example.getName().startsWith("AQ$")) { //oracle AQ tablesreturn true;} else //noinspection HardCodedStringLiteralif (example.getName().startsWith("DR$")) { //oracle index tablesreturn true;} else //noinspection HardCodedStringLiteralif (example.getName().startsWith("SYS_IOT_OVER")) { //oracle system tablereturn true;} else //noinspection HardCodedStringLiteral,HardCodedStringLiteralif ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) {// CORE-1768 - Oracle creates these for spatial indices and will remove them when the index is removed.return true;} else //noinspection HardCodedStringLiteralif (example.getName().startsWith("MLOG$_")) { //Created by materliaized view logs for every table that is part of a materialized view. Not available for DDL operations.return true;} else //noinspection HardCodedStringLiteralif (example.getName().startsWith("RUPD$_")) { //Created by materialized view log tables using primary keys. Not available for DDL operations.return true;} else //noinspection HardCodedStringLiteralif (example.getName().startsWith("WM$_")) { //Workspace Manager backup tables.return true;} else //noinspection HardCodedStringLiteralif ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) { //This table contains the name of the Java object, the date it was loaded, and has a BLOB column to store the Java object.return true;} else //noinspection HardCodedStringLiteralif ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) { //This is a hash table that tracks the loading of Java objects into a schema.return true;} else //noinspection HardCodedStringLiteralif (example.getName().startsWith("ISEQ$$_")) { //System-generated sequencereturn true;} else //noinspection HardCodedStringLiteralif (example.getName().startsWith("USLOG$")) { //for update materialized viewreturn true;} else if (example.getName().startsWith("SYS_FBA")) { //for Flashback tablesreturn true;}}return super.isSystemObject(example);}@Overridepublic boolean supportsAutoIncrement() {// Oracle supports Identity beginning with version 12cboolean isAutoIncrementSupported = false;try {if (getDatabaseMajorVersion() >= 12) {isAutoIncrementSupported = true;}// Returning true will generate create table command with 'IDENTITY' clause, example:// CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) GENERATED BY DEFAULT AS IDENTITY NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey));// While returning false will continue to generate create table command without 'IDENTITY' clause, example:// CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey));} catch (DatabaseException ex) {isAutoIncrementSupported = false;}return isAutoIncrementSupported;}//public Set findUniqueConstraints(String schema) throws DatabaseException {//Set returnSet = new HashSet();////List maps = new Executor(this).queryForList(new RawSqlStatement("SELECT UC.CONSTRAINT_NAME, UCC.TABLE_NAME, UCC.COLUMN_NAME FROM USER_CONSTRAINTS UC, USER_CONS_COLUMNS UCC WHERE UC.CONSTRAINT_NAME=UCC.CONSTRAINT_NAME AND CONSTRAINT_TYPE='U' ORDER BY UC.CONSTRAINT_NAME"));////UniqueConstraint constraint = null;//for (Map map : maps) {//if (constraint == null || !constraint.getName().equals(constraint.getName())) {//returnSet.add(constraint);//Table table = new Table((String) map.get("TABLE_NAME"));//constraint = new UniqueConstraint(map.get("CONSTRAINT_NAME").toString(), table);//}//}//if (constraint != null) {//returnSet.add(constraint);//}////return returnSet;//}@Overridepublic boolean supportsRestrictForeignKeys() {return false;}@Overridepublic int getDataTypeMaxParameters(String dataTypeName) {//noinspection HardCodedStringLiteralif ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) {return 0;}//noinspection HardCodedStringLiteralif ("BINARY_DOUBLE".equals(dataTypeName.toUpperCase())) {return 0;}return super.getDataTypeMaxParameters(dataTypeName);}public String getSystemTableWhereClause(String tableNameColumn) {List<String> clauses = new ArrayList<String>(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 ") + ")";}@Overridepublic boolean jdbcCallsCatalogsSchemas() {return true;}public Set<String> getUserDefinedTypes() {if (userDefinedTypes == null) {userDefinedTypes = new HashSet<>();if ((getConnection() != null) && !(getConnection() instanceof OfflineConnection)) {try {try {//noinspection HardCodedStringLiteraluserDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class));} catch (DatabaseException e) { //fall back to USER_TYPES if the user cannot see ALL_TYPES//noinspection HardCodedStringLiteraluserDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class));}} catch (DatabaseException e) {//ignore error}}}return userDefinedTypes;}@Overridepublic String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) {//noinspection HardCodedStringLiteralif ((databaseFunction != null) && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) {return databaseFunction.toString();}if ((databaseFunction instanceof SequenceNextValueFunction) || (databaseFunction instanceofSequenceCurrentValueFunction)) {String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction);// replace "myschema.my_seq".nextval with "myschema"."my_seq".nextvalreturn quotedSeq.replaceFirst("\"([^\\.\"]+)\\.([^\\.\"]+)\"", "\"$1\".\"$2\"");}return super.generateDatabaseFunctionValue(databaseFunction);}@Overridepublic ValidationErrors validate() {ValidationErrors errors = super.validate();DatabaseConnection connection = getConnection();if ((connection == null) || (connection instanceof OfflineConnection)) {//noinspection HardCodedStringLiteralScope.getCurrentScope().getLog(getClass()).info("Cannot validate offline database");return errors;}if (!canAccessDbaRecycleBin()) {errors.addWarning(getDbaRecycleBinWarning());}return errors;}public String getDbaRecycleBinWarning() {//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,// HardCodedStringLiteral//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteralreturn "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" +"\n" +"The user you used to connect to the database (" + 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 " + getConnection().getConnectionUserName() + ";";}public boolean canAccessDbaRecycleBin() {if (canAccessDbaRecycleBin == null) {DatabaseConnection connection = getConnection();if ((connection == null) || (connection instanceof OfflineConnection)) {return false;}Statement statement = null;try {statement = ((JdbcConnection) connection).createStatement();@SuppressWarnings("HardCodedStringLiteral") ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1");resultSet.close(); //don't need to do anything with the result set, just make sure statement ran.this.canAccessDbaRecycleBin = true;} catch (Exception e) {//noinspection HardCodedStringLiteralif ((e instanceof SQLException) && e.getMessage().startsWith("ORA-00942")) { //ORA-00942: table or view does not existthis.canAccessDbaRecycleBin = false;} else {//noinspection HardCodedStringLiteralScope.getCurrentScope().getLog(getClass()).warning("Cannot check dba_recyclebin access", e);this.canAccessDbaRecycleBin = false;}} finally {JdbcUtils.close(null, statement);}}return canAccessDbaRecycleBin;}@Overridepublic boolean supportsNotNullConstraintNames() {return true;}/** * Tests if the given String would be a valid identifier in Oracle DBMS. In Oracle, a valid identifier has * the following form (case-insensitive comparison): * 1st character: A-Z * 2..n characters: A-Z0-9$_# * The maximum length of an identifier differs by Oracle version and object type. */public boolean isValidOracleIdentifier(String identifier, Class<? extends DatabaseObject> type) {if ((identifier == null) || (identifier.length() < 1))return false;if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$"))return false;/* * @todo It seems we currently do not have a class for tablespace identifiers, and all other classes * we do know seem to be supported as 12cR2 long identifiers, so: */return (identifier.length() <= LONG_IDENTIFIERS_LEGNTH);}/** * Returns the maximum number of bytes (NOT: characters) for an identifier. For Oracle <=12c Release 20, this * is 30 bytes, and starting from 12cR2, up to 128 (except for tablespaces, PDB names and some other rather rare * object types). * * @return the maximum length of an object identifier, in bytes */public int getIdentifierMaximumLength() {try {if (getDatabaseMajorVersion() < ORACLE_12C_MAJOR_VERSION) {return SHORT_IDENTIFIERS_LENGTH;} else if ((getDatabaseMajorVersion() == ORACLE_12C_MAJOR_VERSION) && (getDatabaseMinorVersion() <= 1)) {return SHORT_IDENTIFIERS_LENGTH;} else {return LONG_IDENTIFIERS_LEGNTH;}} catch (DatabaseException ex) {throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex);}}}

OracleDatabase源码位置如下:

  1. 修改liquibase.datatype.core.BooleanType类,以支持达梦的bit类型与java的Boolean类型的转换,同理在java文件下创建BooleanType类,类的全路径为liquibase.datatype.core.BooleanType,将下面代码粘贴进去即可。
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package liquibase.datatype.core;import java.util.Locale;import liquibase.change.core.LoadDataChange.LOAD_DATA_TYPE;import liquibase.database.Database;import liquibase.database.core.DmDatabase;import liquibase.database.core.*;import liquibase.datatype.DataTypeInfo;import liquibase.datatype.DatabaseDataType;import liquibase.datatype.LiquibaseDataType;import liquibase.exception.UnexpectedLiquibaseException;import liquibase.statement.DatabaseFunction;import liquibase.util.StringUtil;@DataTypeInfo(name = "boolean",aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"},minParameters = 0,maxParameters = 0,priority = 1)public class BooleanType extends LiquibaseDataType {public BooleanType() {}public DatabaseDataType toDatabaseDataType(Database database) {String originalDefinition = StringUtil.trimToEmpty(getRawDefinition());if ((database instanceof Firebird3Database)) {return new DatabaseDataType("BOOLEAN");}if ((database instanceof AbstractDb2Database) || (database instanceof FirebirdDatabase)) {return new DatabaseDataType("SMALLINT");} else if (database instanceof MSSQLDatabase) {return new DatabaseDataType(database.escapeDataTypeName("bit"));} else if (database instanceof MySQLDatabase) {if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {return new DatabaseDataType("BIT", getParameters());}return new DatabaseDataType("BIT", 1);} else if (database instanceof OracleDatabase) {return new DatabaseDataType("NUMBER", 1);} else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) {return new DatabaseDataType("BIT");} else if (database instanceof DerbyDatabase) {if (((DerbyDatabase) database).supportsBooleanDataType()) {return new DatabaseDataType("BOOLEAN");} else {return new DatabaseDataType("SMALLINT");}} else if (database.getClass().isAssignableFrom(DB2Database.class)) {if (((DB2Database) database).supportsBooleanDataType())return new DatabaseDataType("BOOLEAN");elsereturn new DatabaseDataType("SMALLINT");} else if (database instanceof HsqlDatabase) {return new DatabaseDataType("BOOLEAN");} else if (database instanceof PostgresDatabase) {if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {return new DatabaseDataType("BIT", getParameters());}} else if(database instanceof DmDatabase) {return new DatabaseDataType("bit");}return super.toDatabaseDataType(database);}public String objectToSql(Object value, Database database) {if (value != null && !"null".equals(value.toString().toLowerCase(Locale.US))) {String returnValue;if (value instanceof String) {value = ((String)value).replaceAll("'", "");if (!"true".equals(((String)value).toLowerCase(Locale.US)) && !"1".equals(value) && !"b'1'".equals(((String)value).toLowerCase(Locale.US)) && !"t".equals(((String)value).toLowerCase(Locale.US)) && !((String)value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) {if (!"false".equals(((String)value).toLowerCase(Locale.US)) && !"0".equals(value) && !"b'0'".equals(((String)value).toLowerCase(Locale.US)) && !"f".equals(((String)value).toLowerCase(Locale.US)) && !((String)value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) {throw new UnexpectedLiquibaseException("Unknown boolean value: " + value);}returnValue = this.getFalseBooleanValue(database);} else {returnValue = this.getTrueBooleanValue(database);}} else if (value instanceof Long) {if (Long.valueOf(1L).equals(value)) {returnValue = this.getTrueBooleanValue(database);} else {returnValue = this.getFalseBooleanValue(database);}} else if (value instanceof Number) {if (!value.equals(1) && !"1".equals(value.toString()) && !"1.0".equals(value.toString())) {returnValue = this.getFalseBooleanValue(database);} else {returnValue = this.getTrueBooleanValue(database);}} else {if (value instanceof DatabaseFunction) {return value.toString();}if (!(value instanceof Boolean)) {throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value");}if ((Boolean)value) {returnValue = this.getTrueBooleanValue(database);} else {returnValue = this.getFalseBooleanValue(database);}}return returnValue;} else {return null;}}protected boolean isNumericBoolean(Database database) {if (database instanceof DerbyDatabase) {return !((DerbyDatabase) database).supportsBooleanDataType();} else if (database.getClass().isAssignableFrom(DB2Database.class)) {return !((DB2Database) database).supportsBooleanDataType();}return (database instanceof Db2zDatabase) || (database instanceof DB2Database) || (database instanceof FirebirdDatabase) || (database instanceofMSSQLDatabase) || (database instanceof MySQLDatabase) || (database instanceof OracleDatabase) ||(database instanceof SQLiteDatabase) || (database instanceof SybaseASADatabase) || (database instanceofSybaseDatabase) || (database instanceof DmDatabase);}public String getFalseBooleanValue(Database database) {if (this.isNumericBoolean(database)) {return "0";} else {return database instanceof InformixDatabase " />"'f'" : "FALSE";}}public String getTrueBooleanValue(Database database) {if (this.isNumericBoolean(database)) {return "1";} else {return database instanceof InformixDatabase ? "'t'" : "TRUE";}}public LOAD_DATA_TYPE getLoadTypeName() {return LOAD_DATA_TYPE.BOOLEAN;}}
  1. 修改resources/META-INF.services/liquibase.database.Database内容,添加DmDatabase支持liquibase.database.core.DmDatabase,同理在自己项目中的resources目录下创建对应的文件,粘贴对应的内容。
liquibase.database.core.CockroachDatabaseliquibase.database.core.DB2Databaseliquibase.database.core.Db2zDatabaseliquibase.database.core.DerbyDatabaseliquibase.database.core.Firebird3Databaseliquibase.database.core.FirebirdDatabaseliquibase.database.core.H2Databaseliquibase.database.core.HsqlDatabaseliquibase.database.core.InformixDatabaseliquibase.database.core.Ingres9Databaseliquibase.database.core.MSSQLDatabaseliquibase.database.core.MariaDBDatabaseliquibase.database.core.MockDatabaseliquibase.database.core.MySQLDatabaseliquibase.database.core.OracleDatabaseliquibase.database.core.PostgresDatabaseliquibase.database.core.SQLiteDatabaseliquibase.database.core.SybaseASADatabaseliquibase.database.core.SybaseDatabaseliquibase.database.core.DmDatabaseliquibase.database.core.UnsupportedDatabase

修改后项目中的结构如下,红框为新增项:

总结

在使用flowable集成达梦8时如果对启动项目生成flowable相关表没有需求的建议使用以下方案:

  1. 使用DM数据迁移工具迁移表及数据,具体操作方式可以看我上一篇博客:数据迁移到DM8数据库教程。
  2. 迁移完毕后,可以导出sql脚本,执行脚本来创建flowable相关表。

如果对自动生成表有要求的话,并且使用该博客方法无解,可以参考如下方案:

  1. 降低Flwoable版本。
  2. Flowable依赖中排除导致生成表错误的依赖,如果没有用到对应表的功能排除即可。
  3. 如果不能排除对应依赖,可以参考该博客修改Flowable源码,第二步的操作来排查。