当前位置: 首页 > news >正文

flowable适配达梦数据库

文章目录

  • 适配相关问题
    • 无法从数据库产品名称“DM DBMS”中推断数据库类型
      • 分析
      • 解决
    • 构建ibatis SqlSessionFactory时出错:inStream参数为null
      • 分析
      • 解决
    • liquibase相关问题
      • 问题一:不支持的数据库 Error executing SQL call current_schema: 无法解析的成员访问表达式[CURRENT_SCHEMA]
        • 分析
        • 解决
      • 问题二:找不到 setRemarksReporting 方法
        • 解决
      • 问题三:存储过程问题问题,Cannot read from DBMS_UTILITY.DB_VERSION: 无效的方法名[DB_VERSION]
        • 分析
        • 解决
      • 问题四: 更新事件注册表引擎表时出错、初始化事件注册表数据模型时出错
        • 解决
      • 问题五:验证事件注册表引擎架构时出错
        • 解决
    • 数据库版本问题version mismatch: library version is '7.0.1.1', db version is 7.0.0.0
      • 解决
    • 启动流程相关问题
      • 解决
    • 服务启动成功日志

网上flowable7新版本适配文档较少,以下使用部署flowable7源码方式说明相关适配问题。

适配相关问题

无法从数据库产品名称“DM DBMS”中推断数据库类型

分析

先查看较为完整的堆栈信息调用流程看一下怎么个事

实例化bean 类型为class com.xxxx.workflow.service.impl.ProcessServiceImp
在这里插入图片描述

通过反射实例化后,填充属性,依赖注入

主要看注入flowable内置的Service
在这里插入图片描述
解析taskService字段

解析依赖关系

根据beanName(taskServiceBean)和type(org.flowable.engine.TaskService)得到要注入的实例

给taskService属性赋值

以上执行完就给taskSerive字段属性赋值了,报错是在执行
org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate方法时,也就是实例化taskService的时候

实例化taskService

从bena定义信息中可以看出有工厂方法 返回的是taskService

所以通过工厂方法实例化

得到候选实例的参数

解析注入参数processEngine

根据beanName(processEngine)和type(org.flowable.engine.ProcessEngine)得到要注入的实例

实例化processEngine

从bean定义信息中可以看出是通过 ProcessEngineFactoryBean 定制化创建的ProcessEngine,在ProcessEngineFactoryBean的getOject方法中会构建流程引擎

public class ProcessEngineFactoryBean implements FactoryBean<ProcessEngine>, DisposableBean, ApplicationContextAware {protected ProcessEngineConfigurationImpl processEngineConfiguration;protected ApplicationContext applicationContext;protected ProcessEngine processEngine;@Overridepublic void destroy() throws Exception {if (processEngine != null) {processEngine.close();}}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overridepublic ProcessEngine getObject() throws Exception {configureExternallyManagedTransactions();if (processEngineConfiguration.getBeans() == null) {processEngineConfiguration.setBeans(new SpringBeanFactoryProxyMap(applicationContext));}// 构建流程引擎this.processEngine = processEngineConfiguration.buildProcessEngine();return this.processEngine;}protected void configureExternallyManagedTransactions() {if (processEngineConfiguration instanceof SpringProcessEngineConfiguration) { // remark: any config can be injected, so we cannot have SpringConfiguration as memberSpringProcessEngineConfiguration engineConfiguration = (SpringProcessEngineConfiguration) processEngineConfiguration;if (engineConfiguration.getTransactionManager() != null) {processEngineConfiguration.setTransactionsExternallyManaged(true);}}}@Overridepublic Class<ProcessEngine> getObjectType() {return ProcessEngine.class;}@Overridepublic boolean isSingleton() {return true;}public ProcessEngineConfigurationImpl getProcessEngineConfiguration() {return processEngineConfiguration;}public void setProcessEngineConfiguration(ProcessEngineConfigurationImpl processEngineConfiguration) {this.processEngineConfiguration = processEngineConfiguration;}
}

通过ProcessEngineFactoryBean的getOject获取processEngine实例的对象

重点在这里

org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl#buildProcessEngine 构建流程引擎

org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl#init 初始化

org.flowable.common.engine.impl.AbstractEngineConfiguration#initDataSource 初始化数据源

org.flowable.common.engine.impl.AbstractEngineConfiguration#initDatabaseType 初始化数据源类型 databaseProductName为DM DBMS

从org.flowable.common.engine.impl.AbstractEngineConfiguration#getDefaultDatabaseTypeMappings 从数据库类型映射中获取DM DBMS看看有没有,没有则抛出异常

    public void initDatabaseType() {Connection connection = null;try {connection = dataSource.getConnection();DatabaseMetaData databaseMetaData = connection.getMetaData();//databaseProductName为DM DBMSString databaseProductName = databaseMetaData.getDatabaseProductName(); logger.debug("database product name: '{}'", databaseProductName);// 如果是PostgreSQL,做一下处理...if (PRODUCT_NAME_POSTGRES.equalsIgnoreCase(databaseProductName)) {try (PreparedStatement preparedStatement = connection.prepareStatement("select version() as version;");ResultSet resultSet = preparedStatement.executeQuery()) {String version = null;if (resultSet.next()) {version = resultSet.getString("version");}if (StringUtils.isNotEmpty(version) && version.toLowerCase().startsWith(PRODUCT_NAME_CRDB.toLowerCase())) {databaseProductName = PRODUCT_NAME_CRDB;logger.info("CockroachDB version '{}' detected", version);}}}//从org.flowable.common.engine.impl.AbstractEngineConfiguration#getDefaultDatabaseTypeMappings获取DM DBMS看看有没有databaseType = databaseTypeMappings.getProperty(databaseProductName);if (databaseType == null) {throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'");}logger.debug("using database type: {}", databaseType);} catch (SQLException e) {throw new RuntimeException("Exception while initializing Database connection", e);} finally {try {if (connection != null) {connection.close();}} catch (SQLException e) {logger.error("Exception while closing the Database connection", e);}}if (DATABASE_TYPE_MSSQL.equals(databaseType)) {maxNrOfStatementsInBulkInsert = DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER;}}
    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);return databaseTypeMappings;}

解决

添加达梦数据库类型映射

org.flowable.common.engine.impl.AbstractEngineConfiguration

构建ibatis SqlSessionFactory时出错:inStream参数为null

分析

定位

上面流程初始化了数据源,数据源类型等

在org.flowable.common.engine.impl.AbstractEngineConfiguration#initSqlSessionFactory初始化SqlSessionFactory,会根据数据源类型读取配置org/flowable/common/db/properties/dm.properties,没有dm的配置(mybatis分页相关配置),报InputStream为null

 public void initSqlSessionFactory() {if (sqlSessionFactory == null) {InputStream inputStream = null;try {//  读取org/flowable/db/mapping/mappings.xmlinputStream = getMyBatisXmlConfigurationStream();Environment environment = new Environment("default", transactionFactory, dataSource);Reader reader = new InputStreamReader(inputStream);Properties properties = new Properties();properties.put("prefix", databaseTablePrefix);String wildcardEscapeClause = "";if ((databaseWildcardEscapeCharacter != null) && (databaseWildcardEscapeCharacter.length() != 0)) {wildcardEscapeClause = " escape '" + databaseWildcardEscapeCharacter + "'";}properties.put("wildcardEscapeClause", wildcardEscapeClause);// set default propertiesproperties.put("limitBefore", "");properties.put("limitAfter", "");properties.put("limitBetween", "");properties.put("limitBeforeNativeQuery", "");properties.put("limitAfterNativeQuery", "");properties.put("blobType", "BLOB");properties.put("boolValue", "TRUE");if (databaseType != null) {// 读取org/flowable/common/db/properties/dm.properties 设置propertiesproperties.load(getResourceAsStream(pathToEngineDbProperties()));}// Configuration configuration = initMybatisConfiguration(environment, reader, properties);sqlSessionFactory = new DefaultSqlSessionFactory(configuration);} catch (Exception e) {throw new FlowableException("Error while building ibatis SqlSessionFactory: " + e.getMessage(), e);} finally {IoUtil.closeSilently(inputStream);}} else {applyCustomMybatisCustomizations(sqlSessionFactory.getConfiguration());}}

解决

添加dm.properties 参考oracle

limitBefore=select RES.* from ( select RES.*, rownum as rnum from (
limitAfter= ) RES where ROWNUM < #{lastRow} ) RES where rnum >= #{firstRow}
boolValue=1

liquibase相关问题

liquibase版本为4.20.0

问题一:不支持的数据库 Error executing SQL call current_schema: 无法解析的成员访问表达式[CURRENT_SCHEMA]

liquibase.exception.DatabaseException: Error executing SQL call current_schema: 第1 行附近出现错误:

无法解析的成员访问表达式[CURRENT_SCHEMA]

分析

定位

org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl#buildProcessEngine

org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl#init

org.flowable.common.engine.impl.AbstractEngineConfiguration#configuratorsAfterInit

org.flowable.eventregistry.impl.EventRegistryEngineConfiguration#buildEventRegistryEngine

org.flowable.eventregistry.impl.EventRegistryEngineConfiguration#init

org.flowable.eventregistry.impl.EventRegistryEngineImpl 实例化

org.flowable.eventregistry.impl.db.EventDbSchemaManager#initSchema

org.flowable.common.engine.impl.db.LiquibaseBasedSchemaManager#createLiquibaseInstance 创建Liquibase实例

    protected Liquibase createLiquibaseInstance(LiquibaseDatabaseConfiguration databaseConfiguration) throws SQLException {Connection jdbcConnection = null;boolean closeConnection = false;try {CommandContext commandContext = Context.getCommandContext();if (commandContext == null) {jdbcConnection = databaseConfiguration.getDataSource().getConnection();closeConnection = true;} else {jdbcConnection = commandContext.getSession(DbSqlSession.class).getSqlSession().getConnection();}if (!jdbcConnection.getAutoCommit()) {jdbcConnection.commit();}// 创建jdbc连接DatabaseConnection connection = new JdbcConnection(jdbcConnection);// 创建数据库实例Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);// 设置数据库更改日志表名称  FLW_EV_DATABASECHANGELOGdatabase.setDatabaseChangeLogTableName(changeLogPrefix + database.getDatabaseChangeLogTableName());// 设置数据库更改日志锁定表名称  FLW_EV_DATABASECHANGELOGLOCKdatabase.setDatabaseChangeLogLockTableName(changeLogPrefix + database.getDatabaseChangeLogLockTableName());String databaseSchema = databaseConfiguration.getDatabaseSchema();if (StringUtils.isNotEmpty(databaseSchema)) {database.setDefaultSchemaName(databaseSchema);database.setLiquibaseSchemaName(databaseSchema);}String databaseCatalog = databaseConfiguration.getDatabaseCatalog();if (StringUtils.isNotEmpty(databaseCatalog)) {database.setDefaultCatalogName(databaseCatalog);database.setLiquibaseCatalogName(databaseCatalog);}return new Liquibase(changeLogFile, new ClassLoaderResourceAccessor(), database);} catch (Exception e) {// We only close the connection if an exception occurred, otherwise the Liquibase instance cannot be usedif (jdbcConnection != null && closeConnection) {jdbcConnection.close();}throw new FlowableException("Error creating " + context + " liquibase instance", e);}}

liquibase.database.DatabaseFactory#findCorrectDatabaseImplementation 实例化所有实现了AbstractJdbcDatabase的Database,根据数据库连接获取数据库实现

    public Database findCorrectDatabaseImplementation(DatabaseConnection connection) throws DatabaseException {// 实例化所有实现了AbstractJdbcDatabase的DatabaseSortedSet<Database> foundDatabases = new TreeSet(new DatabaseComparator());Iterator var3 = this.getImplementedDatabases().iterator();while(var3.hasNext()) {Database implementedDatabase = (Database)var3.next();if (connection instanceof OfflineConnection) {if (((OfflineConnection)connection).isCorrectDatabaseImplementation(implementedDatabase)) {foundDatabases.add(implementedDatabase);}// 通过连接的数据库产品名判断 return "DM DBMS".equalsIgnoreCase(conn.getDatabaseProductName());} else if (implementedDatabase.isCorrectDatabaseImplementation(connection)) {foundDatabases.add(implementedDatabase);}}// 没有找到实现if (foundDatabases.isEmpty()) {LOG.warning("Unknown database: " + connection.getDatabaseProductName());// 返回一个不支持的数据库UnsupportedDatabase unsupportedDB = new UnsupportedDatabase();// 设置连接unsupportedDB.setConnection(connection);return unsupportedDB;} else {Database returnDatabase;try {returnDatabase = (Database)((Database)foundDatabases.iterator().next()).getClass().getConstructor().newInstance();} catch (Exception var5) {throw new UnexpectedLiquibaseException(var5);}returnDatabase.setConnection(connection);return returnDatabase;}}

没有达梦数据库实现,返回一个不支持的数据库实例

设置连接时,执行报错

解决

liquibase.database.DatabaseFactory 查看实例化Database方式

liquibase.servicelocator.StandardServiceLocator#findInstances 通过Java SPI机制实现

所以根据SPI实现方式 创建达梦数据库实现 (暂且放在业务工程中,后面可以搞一个单独的集成工程 或 直接添加到flowable源码中)

创建AbstractJdbcDatabase实现类DMDatabase 参考liquibase.database.core.OracleDatabase实现修改

package com.xxxx.workflow.database;/*** @Author Chow* @Date 2024/12/27 17:44* @Version 1.0* @description**/
public class DMDatabase extends AbstractJdbcDatabase {public static final Pattern PROXY_USER = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*");/*** 产品名称*/public static final String PRODUCT_NAME = "DM DBMS";private static 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 Set<String> reservedWords = new HashSet();private Set<String> userDefinedTypes;private Map<String, String> savedSessionNlsSettings;private Boolean canAccessDbaRecycleBin;private Integer databaseMajorVersion;private Integer databaseMinorVersion;public DMDatabase() {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";}@Overridepublic 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 DMDatabase: " + 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 DMDatabase: ");}} catch (Exception var7) {Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on DMDatabase: " + var7.getCause().getMessage());}}}@Overridepublic 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 var29) {throw new UnexpectedLiquibaseException(var29);}if (sqlConn != null) {this.tryProxySession(conn.getURL(), sqlConn);try {this.reservedWords.addAll(Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*")));} catch (SQLException var28) {Scope.getCurrentScope().getLog(this.getClass()).info("Could get sql keywords on DMDatabase: " + var28.getMessage());}/*** 在Oracle数据库中,setRemarksReporting是一个用于设置数据库对象(如表、视图、列等)注释或描述的方法。这通常用于生成数据库文档。*/try {Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE);method.setAccessible(true);method.invoke(sqlConn, true);} catch (Exception var27) {Scope.getCurrentScope().getLog(this.getClass()).info("Could not set remarks reporting on DMDatabase: " + var27.getMessage());}CallableStatement statement = null;// 基于oracle的实现修改  存储过程问题,无效的方法名[DB_VERSION]  这里直接写死String sql;try {DatabaseMetaData metaData = sqlConn.getMetaData();Connection connection = metaData.getConnection();Connection connection1 = connection.getMetaData().getConnection();//String compatibleVersion = "11.2.0.4.0"; //这个是oracleDatabase 当前使用的数据库版本String compatibleVersion = "8.1.3.100";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 var25) {sql = "Cannot read from DBMS_UTILITY.DB_VERSION: " + var25.getMessage();Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on DMDatabase, assuming not running in any sort of compatibility mode: " + sql);} finally {JdbcUtil.closeStatement(statement);}if (GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue() != null) {int timeoutValue = (Integer) GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue();Scope.getCurrentScope().getLog(this.getClass()).fine("Setting DDL_LOCK_TIMEOUT value to " + timeoutValue);sql = "ALTER SESSION SET DDL_LOCK_TIMEOUT=" + timeoutValue;PreparedStatement ddlLockTimeoutStatement = null;try {ddlLockTimeoutStatement = sqlConn.prepareStatement(sql);ddlLockTimeoutStatement.execute();} catch (SQLException var23) {Scope.getCurrentScope().getUI().sendErrorMessage("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + var23.getMessage(), var23);Scope.getCurrentScope().getLog(this.getClass()).warning("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + var23.getMessage(), var23);} finally {JdbcUtil.closeStatement(ddlLockTimeoutStatement);}}}}super.setConnection(conn);}/*** 简称* @return*/@Overridepublic String getShortName() {return "dm";}/*** 默认数据库产品名称* @return*/@Overrideprotected String getDefaultDatabaseProductName() {return "DM DBMS";}@Overridepublic int getDatabaseMajorVersion() throws DatabaseException {return this.databaseMajorVersion == null ? super.getDatabaseMajorVersion() : this.databaseMajorVersion;}@Overridepublic int getDatabaseMinorVersion() throws DatabaseException {return this.databaseMinorVersion == null ? super.getDatabaseMinorVersion() : this.databaseMinorVersion;}/*** 端口* @return*/@Overridepublic Integer getDefaultPort() {return 5236;}@Overridepublic String getJdbcCatalogName(CatalogAndSchema schema) {return null;}@Overridepublic String getJdbcSchemaName(CatalogAndSchema schema) {return this.correctObjectName(schema.getCatalogName() == null ? schema.getSchemaName() : schema.getCatalogName(), Schema.class);}@Overrideprotected 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.toUpperCase().equals("BY DEFAULT")) {generationStrategy = generationType + " ON NULL";}return String.format(autoIncrementClause, generationStrategy);}}@Overridepublic String generatePrimaryKeyName(String tableName) {return tableName.length() > 27 ? "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27) : "PK_" + tableName.toUpperCase(Locale.US);}@Overridepublic boolean supportsInitiallyDeferrableColumns() {return true;}@Overridepublic boolean isReservedWord(String objectName) {return this.reservedWords.contains(objectName.toUpperCase());}@Overridepublic boolean supportsSequences() {return true;}@Overridepublic boolean supportsSchemas() {return false;}@Overrideprotected 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 (String) ((ExecutorService) 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;}}}/*** 根据数据库连接判断是否是该数据库实现* {@link liquibase.database.DatabaseFactory#findCorrectDatabaseImplementation(liquibase.database.DatabaseConnection)}* {@link Database#isCorrectDatabaseImplementation(DatabaseConnection)}* @param conn* @return* @throws DatabaseException*/@Overridepublic boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {return "DM DBMS".equalsIgnoreCase(conn.getDatabaseProductName());}/*** jdbc 驱动* @param url* @return*/@Overridepublic String getDefaultDriver(String url) {return url.startsWith("jdbc:dm") ? "dm.jdbc.driver.DmDriver" : null;}@Overridepublic String getDefaultCatalogName() {String defaultCatalogName = super.getDefaultCatalogName();if (Boolean.TRUE.equals(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getCurrentValue())) {return defaultCatalogName;} else {return defaultCatalogName == null ? null : defaultCatalogName.toUpperCase(Locale.US);}}@Overridepublic 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;}}@Overridepublic 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);}}@Overridepublic boolean supportsTablespaces() {return true;}@Overridepublic boolean supportsAutoIncrement() {boolean isAutoIncrementSupported = false;try {if (this.getDatabaseMajorVersion() >= 12) {isAutoIncrementSupported = true;}} catch (DatabaseException var3) {isAutoIncrementSupported = false;}return isAutoIncrementSupported;}@Overridepublic boolean supportsRestrictForeignKeys() {return false;}@Overridepublic int getDataTypeMaxParameters(String dataTypeName) {if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) {return 0;} else {return "BINARY_DOUBLE".equals(dataTypeName.toUpperCase()) ? 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 '" + (String) clauses.get(i) + "%'");}return "(" + StringUtil.join(clauses, " AND ") + ")";}@Overridepublic 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(((ExecutorService) 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(((ExecutorService) 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;}@Overridepublic 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\"");}}@Overridepublic 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 {JdbcUtil.close((ResultSet) null, statement);}}return this.canAccessDbaRecycleBin;}@Overridepublic 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 DM database version number", var2);}}
}

新建META-INF/services/liquibase.database.Database文件,添加DMDatabase全路径名

在这里插入图片描述

再次运行可以找到dm数据库实现

进入else

问题二:找不到 setRemarksReporting 方法

Could not set remarks reporting on OracleDatabase: jdk.proxy2.$Proxy147.setRemarksReporting(boolean)

Method threw ‘java.lang.NoSuchMethodException’ exception. 找不到 setRemarksReporting 方法

定位

com.xxxx.workflow.database.DMDatabase#setConnection

解决

反射调用了 JDBC 连接对象setRemarksReporting 方法

Oracle数据库中 setRemarksReporting用于设置数据库对象注释的方法,用来生成数据库文档。

这个dm的数据库实现可以直接注释

问题三:存储过程问题问题,Cannot read from DBMS_UTILITY.DB_VERSION: 无效的方法名[DB_VERSION]

Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: Cannot read from DBMS_UTILITY.DB_VERSION: 第1 行附近出现错误:无效的方法名[DB_VERSION]

分析

定位

com.xxxx.workflow.database.DMDatabase#setConnection

调用了存储过程

解决

达梦没有这个存储过程 这些把值写死

   @Overridepublic 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 var29) {throw new UnexpectedLiquibaseException(var29);}if (sqlConn != null) {this.tryProxySession(conn.getURL(), sqlConn);try {this.reservedWords.addAll(Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*")));} catch (SQLException var28) {Scope.getCurrentScope().getLog(this.getClass()).info("Could get sql keywords on OracleDatabase: " + var28.getMessage());}try {Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE);method.setAccessible(true);method.invoke(sqlConn, true);} catch (Exception var27) {Scope.getCurrentScope().getLog(this.getClass()).info("Could not set remarks reporting on OracleDatabase: " + var27.getMessage());}CallableStatement statement = null;String sql;try {DatabaseMetaData metaData = sqlConn.getMetaData();Connection connection = metaData.getConnection();Connection connection1 = connection.getMetaData().getConnection();//String compatibleVersion = "11.2.0.4.0";  //这个是oracleDatabase 当前使用的数据库版本String compatibleVersion = "8.1.3.100";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 var25) {sql = "Cannot read from DBMS_UTILITY.DB_VERSION: " + var25.getMessage();Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + sql);} finally {JdbcUtil.closeStatement(statement);}if (GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue() != null) {int timeoutValue = (Integer) GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue();Scope.getCurrentScope().getLog(this.getClass()).fine("Setting DDL_LOCK_TIMEOUT value to " + timeoutValue);sql = "ALTER SESSION SET DDL_LOCK_TIMEOUT=" + timeoutValue;PreparedStatement ddlLockTimeoutStatement = null;try {ddlLockTimeoutStatement = sqlConn.prepareStatement(sql);ddlLockTimeoutStatement.execute();} catch (SQLException var23) {Scope.getCurrentScope().getUI().sendErrorMessage("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + var23.getMessage(), var23);Scope.getCurrentScope().getLog(this.getClass()).warning("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + var23.getMessage(), var23);} finally {JdbcUtil.closeStatement(ddlLockTimeoutStatement);}}}}super.setConnection(conn);}

参考:https://blog.csdn.net/zhangdaiscott/article/details/134547342

问题四: 更新事件注册表引擎表时出错、初始化事件注册表数据模型时出错

解决

将flowable.database-schema-update设置为false

应用启动时不会更新数据库模式,如果数据库模式和 Flowable 版本不匹配,会抛出异常阻止应用启动。

问题五:验证事件注册表引擎架构时出错

liquibase.Liquibase#validate

liquibase.executor.jvm.JdbcExecutor#execute(liquibase.statement.SqlStatement, java.util.List<liquibase.sql.visitor.SqlVisitor>)

解决

liquibase.Liquibase#validate 验证数据库模式与变更日志文件一致性,这里直接注释掉。

数据库版本问题version mismatch: library version is ‘7.0.1.1’, db version is 7.0.0.0

解决

参考flowable源码当前版 修改为对应版本

也可以数据源先使用mysql,再做迁移

启动流程相关问题

由于flowable.database-schema-update设置为false,如果数据表结构等和 Flowable 版本不匹配,不会进行自动更新,当前表直接迁移的是原7.0.0版本的表结构,源码部署的是GA版本的7.0.0分支(实际版本为7.0.0.39),两个版本的初始化表数量一样,但表结构字段有不同。

列找不到

解决

表结构可以先使用mysql生成,迁移到dm

7.0.0.39新增字段

服务启动成功日志

解决以上问题可以启动服务

Closing JDBC Connection [Transaction-aware proxy for target Connection [HikariProxyConnection@1750724866 wrapping dm.jdbc.driver.DmdbConnection@657f1fc3]]
2024-12-30 17:12:19.969 [main] INFO  o.f.i.e.i.IdmEngineImpl -[<init>,52] - IdmEngine default created
2024-12-30 17:12:19.986 [main] INFO  o.f.s.SpringProcessEngineConfiguration -[configuratorsAfterInit,1157] - Executing configure() of class org.flowable.dmn.spring.configurator.SpringDmnEngineConfigurator (priority:200000)
Opening JDBC Connection
2024-12-30 17:12:30.646 [main] INFO  liquibase.database -[log,37] - Could not set remarks reporting on OracleDatabase: jdk.proxy2.$Proxy147.setRemarksReporting(boolean)
Closing JDBC Connection [Transaction-aware proxy for target Connection [HikariProxyConnection@1026432280 wrapping dm.jdbc.driver.DmdbConnection@657f1fc3]]
2024-12-30 17:12:35.406 [main] INFO  o.f.d.e.i.DmnEngineImpl -[<init>,55] - DmnEngine default created
2024-12-30 17:12:35.415 [main] INFO  o.f.s.SpringProcessEngineConfiguration -[configuratorsAfterInit,1157] - Executing configure() of class org.flowable.cmmn.spring.configurator.SpringCmmnEngineConfigurator (priority:500000)
Opening JDBC Connection
2024-12-30 17:12:45.684 [main] INFO  liquibase.database -[log,37] - Could not set remarks reporting on OracleDatabase: jdk.proxy2.$Proxy147.setRemarksReporting(boolean)
Closing JDBC Connection [Transaction-aware proxy for target Connection [HikariProxyConnection@8603972 wrapping dm.jdbc.driver.DmdbConnection@657f1fc3]]
2024-12-30 17:12:52.094 [main] INFO  o.f.c.e.i.CmmnEngineImpl -[<init>,72] - CmmnEngine default created
Opening JDBC Connection
==>  Preparing: select VALUE_ from ACT_GE_PROPERTY where NAME_ = 'schema.version'
==> Parameters: 
<==    Columns: VALUE_
<==        Row: 7.0.1.1
<==      Total: 1
Closing JDBC Connection [Transaction-aware proxy for target Connection [HikariProxyConnection@530287651 wrapping dm.jdbc.driver.DmdbConnection@657f1fc3]]
2024-12-30 17:12:52.443 [main] INFO  o.f.e.i.ProcessEngineImpl -[<init>,89] - ProcessEngine default created
Opening JDBC Connection
==>  Preparing: select count(RES.ID_) from ACT_RE_DEPLOYMENT RES WHERE RES.ENGINE_VERSION_ = ?
==> Parameters: v5(String)
<==    Columns: COUNT(RES.ID_)
<==        Row: 0
<==      Total: 1
2024-12-30 17:12:52.937 [main] INFO  o.f.e.i.c.ValidateV5EntitiesCmd -[execute,43] - Total of v5 deployments found: 0
Closing JDBC Connection [Transaction-aware proxy for target Connection [HikariProxyConnection@871541777 wrapping dm.jdbc.driver.DmdbConnection@657f1fc3]]
Opening JDBC Connection
==>  Preparing: select * from ACT_GE_PROPERTY where NAME_ = ?
==> Parameters: cfg.execution-related-entities-count(String)
<==    Columns: NAME_, VALUE_, REV_
<==        Row: cfg.execution-related-entities-count, true, 1
<==      Total: 1
Closing JDBC Connection [Transaction-aware proxy for target Connection [HikariProxyConnection@1059563867 wrapping dm.jdbc.driver.DmdbConnection@657f1fc3]]
Opening JDBC Connection
==>  Preparing: select * from ACT_GE_PROPERTY where NAME_ = ?
==> Parameters: cfg.task-related-entities-count(String)
<==    Columns: NAME_, VALUE_, REV_
<==        Row: cfg.task-related-entities-count, true, 1
<==      Total: 1....

相关文章:

flowable适配达梦数据库

文章目录 适配相关问题无法从数据库产品名称“DM DBMS”中推断数据库类型分析解决 构建ibatis SqlSessionFactory时出错&#xff1a;inStream参数为null分析解决 liquibase相关问题问题一&#xff1a;不支持的数据库 Error executing SQL call current_schema: 无法解析的成员访…...

Git入门:数据模型 to 底层原理

版本控制系统&#xff08;VCS&#xff09;是软件开发中不可或缺的工具&#xff0c;而Git作为现代版本控制的事实标准&#xff0c;其底层设计远比表面命令更加优雅。本文将从数据模型的角度&#xff0c;揭示Git的核心工作原理。 Git的核心概念 1. 快照&#xff08;Snapshot&am…...

Bootstrap Blazor UI 中 <Table> 组件 <TableColumn> 使用备忘01:EF Core 外码处理

应用场景&#xff1a;将外码转换为对应的文本进行显示、编辑。 例如&#xff0c;有一个【用户】表&#xff0c;其中有一个【用户类型ID】字段&#xff1b;另有一个【用户类型】表&#xff0c;包含【ID】、【名称】等字段。现在要求在 <Table> 组件显示列表中&#xff0c…...

Redis过期数据处理

Redis缓存过期后数据还能恢复吗&#xff1f; Redis缓存过期后&#xff0c;数据通常会被删除&#xff0c;但可以通过以下几种方法尝试恢复数据&#xff1a; 1. 数据备份恢复 RDB 持久化恢复&#xff1a;Redis 提供了 RDB&#xff08;Redis Database Backup&#xff09;持久化…...

零基础学C/C++160——字符串

题目描述 给定两个由小写字母组成的字符串A和B&#xff0c;判断B中的字符是否全部在A中出现。 输入 输入为多组测试数据。 输入数据只有一行&#xff0c;包含2个字符串A和B&#xff0c;每个字符串后面有一个#字符标记&#xff08;#不属于A或B&#xff09;&#xff0c;其中B…...

Spring Boot+Vue项目从零入手

Spring BootVue项目从零入手 一、前期准备 在搭建spring bootvue项目前&#xff0c;我们首先要准备好开发环境&#xff0c;所需相关环境和软件如下&#xff1a; 1、node.js 检测安装成功的方法&#xff1a;node -v 2、vue 检测安装成功的方法&#xff1a;vue -V 3、Visu…...

Linux 命令大全完整版(13)

5.文件管理命令 patch 功能说明&#xff1a;修补文件。语  法&#xff1a;patch [-bceEflnNRstTuvZ][-B <备份字首字符串>][-d <工作目录>][-D <标示符号>][-F <监别列数>][-g <控制数值>][-i <修补文件>][-o <输出文件>][-p &l…...

MySQL面试学习

MySQL 1.事务 事务的4大特性 事务4大特性&#xff1a;原子性、一致性、隔离性、持久性 原⼦性&#xff1a; 事务是最⼩的执⾏单位&#xff0c;不允许分割。事务的原⼦性确保动作要么全部完成&#xff0c;要么全不执行一致性&#xff1a; 执⾏事务前后&#xff0c;数据保持⼀…...

CentOS中shell脚本对多台机器执行下载安装

1.建立免密ssh连接 详情见这篇&#xff1a; CentOS建立ssh免密连接&#xff08;含流程剖析&#xff09;-CSDN博客 2.脚本编写 我这里只是简单写了个demo进行演示&#xff0c;如果服务器很多可以先暂存成文件再逐行读取host进行连接并执行命令 用node1去ssh连接node2和node…...

【Java】多线程和高并发编程(四):阻塞队列(上)基础概念、ArrayBlockingQueue

文章目录 四、阻塞队列1、基础概念1.1 生产者消费者概念1.2 JUC阻塞队列的存取方法 2、ArrayBlockingQueue2.1 ArrayBlockingQueue的基本使用2.2 生产者方法实现原理2.2.1 ArrayBlockingQueue的常见属性2.2.2 add方法实现2.2.3 offer方法实现2.2.4 offer(time,unit)方法2.2.5 p…...

C语言多人聊天室 ---chat(客户端聊天)

head.h #ifndef __HEAD_H #define __HEAD_H// 常用头文件 #include <stdio.h> #include <stdlib.h> #include <string.h>// 网络编程涉及的头文件 #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h>#include <…...

设计模式教程:命令模式(Command Pattern)

1. 什么是命令模式&#xff1f; 命令模式&#xff08;Command Pattern&#xff09;是一种行为型设计模式。它将请求封装成一个对象&#xff0c;从而使你能够用不同的请求、队列和日志请求以及支持可撤销操作。 简单来说&#xff0c;命令模式通过把请求封装成对象的方式解耦了…...

【华三】STP的角色选举(一文讲透)

【华三】STP的角色选举 一、引言二、STP基础概念扫盲三、根桥选举过程详解四、根端口选举过程详解五、指定端口选举过程详解六、阻塞端口七、总结与配置建议七、附录**1. BPDU字段结构图&#xff08;文字描述&#xff09;****2. 华三STP常用命令速查表** 文章总结 一、引言 在…...

Trae+Qt+MSVC环境配置

Trae Trae是字节跳动基于VSCode推出的AI集成开发环境&#xff08;IDE&#xff09;&#xff0c;是一款专为中文开发者深度定制的智能编程工具。其目标是通过AI技术实现从“Copilot”到“Autopilot”的编程模式演进。 类似这样的IDE比如Windsurf、Cursor&#xff0c;都是基于VS…...

SpringSecurity初始化的本质

一、对SpringSecurity初始化的几个疑问 通过前面第一次请求访问的分析我们明白了一个请求就来后的具体处理流程 对于一个请求到来后会通过FilterChainProxy来匹配一个对应的过滤器链来处理该请求。那么这里我们就有几个疑惑。 FilterChainProxy什么时候创建的?过滤器链和对应的…...

3D Gaussian Splatting(3DGS)的核心原理

3D Gaussian Splatting&#xff08;3DGS&#xff09;的核心原理 1. 基本概念 3D Gaussian Splatting&#xff08;3DGS&#xff09; 是一种基于 高斯分布的点云表示与渲染技术&#xff0c;核心思想是将三维场景建模为一系列 可学习的高斯分布&#xff0c;每个高斯分布具有以下…...

Transformers快速入门-学习笔记

一、自然语言处理 NLP 是借助计算机技术研究人类语言的科学自然语言处理发展史 一、不懂语法怎么理解语言 依靠语言学家人工总结文法规则 Chomsky Formal Languages 难点&#xff1a;上下文有关文法 规则增多&#xff0c;存在矛盾 二、只要看得足够多&#xff0c;就能处理语言…...

【Http和Https区别】

概念&#xff1a; 一、Http协议 HTTP&#xff08;超文本传输协议&#xff09;是一种用于传输超媒体文档&#xff08;如HTML&#xff09;的应用层协议&#xff0c;主要用于Web浏览器和服务器之间的通信。http也是客户端和服务器之间请求与响应的标准协议&#xff0c;客户端通常…...

学习路程二 LangChain基本介绍

前面简单调用了一下deepseek的方法&#xff0c;发现有一些疑问和繁琐的问题&#xff0c;需要更多的学习&#xff0c;然后比较流行的就是LangChain这个东西了。 目前大部分企业都是基于 LangChain 、qwen-Agent、lammaIndex框架进行大模型应用开发。LangChain 提供了 Chain、To…...

简识Kafka集群与RocketMQ集群的核心区别

前记&#xff1a;各位潘安、各位子健/各位彦祖、于晏&#xff0c;文字较多&#xff0c;优先看目录。 Kafka集群与RocketMQ集群的核心区别及架构图例说明 一、核心区别对比 特性Kafka 集群RocketMQ 集群设计目标高吞吐量实时日志流系统&#xff08;如日志收集、大数据流水线&a…...

基于Python+django+mysql旅游数据爬虫采集可视化分析推荐系统

2024旅游推荐系统爬虫可视化&#xff08;协同过滤算法&#xff09; 基于Pythondjangomysql旅游数据爬虫采集可视化分析推荐系统 有文档说明 部署文档 视频讲解 ✅️基于用户的协同过滤推荐算法 卖价就是标价~ 项目技术栈 Python语言、Django框架、MySQL数据库、requests网络爬虫…...

9-1. MySQL 性能分析工具的使用——last_query_cost,慢查询日志

9-1. MySQL 性能分析工具的使用——last_query_cost&#xff0c;慢查询日志 文章目录 9-1. MySQL 性能分析工具的使用——last_query_cost&#xff0c;慢查询日志1. 数据库服务器的优化步骤2. 查看系统性能参数3. 统计SQL的查询成本&#xff1a;last_query_cost4. 定位执行慢的…...

网络安全监测探针安装位置 网络安全监测系统

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 软件简介&#xff1a; SockMon(SocketMonitor)网络安全监控系统是一款为电脑专业人员打造的一款出色的安防监控软件。在如今这个恶意软件&#xff0c;攻击&#…...

Git版本控制系统---本地操作(万字详解!)

目录 git基本配置 认识工作区、暂存区、版本库 添加文件--情况一&#xff1a; 添加文件-情况二: 修改文件: 版本回退&#xff1a; git基本配置 1.初始化本地仓库&#xff0c;注意&#xff1a;一定要在一个目录下进行&#xff0c;一般都是新建一个文件夹&#xff0c;在文件…...

forge-1.21.x模组开发(二)给物品添加功能

功能效果 创建一个兑换券&#xff0c;当使用兑换券对着兑换机右键时&#xff0c;获得一条烤鱼 创建兑换券 创建ExchangeCouponsItem.java&#xff0c;继承Item&#xff0c;定义兑换券内容 public class ExchangeCouponsItem extends Item {public ExchangeCouponsItem(Prop…...

elasticsearch在windows上的配置

写在最前面&#xff1a; 上资源 第一步 解压&#xff1a; 第二步 配置两个环境变量 第三步 如果是其他资源需要将标蓝的文件中的内容加一句 xpack.security.enabled: false 不同版本的yaml文件可能配置不同&#xff0c;末尾加这个 xpack.security.enabled: true打开bin目…...

机器学习数学通关指南——拉格朗日乘子法

前言 本文隶属于专栏《机器学习数学通关指南》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见《机器学习数学通关指南》 正文 一句话总结 拉格朗日乘子法…...

Matplotlib,Streamlit,Django大致介绍

Matplotlib&#xff1a;是一个用于创建各种类型的静态、动态和交互式图表的Python绘图库。可以通过pip install matplotlib命令进行安装&#xff0c;安装完成后&#xff0c;在Python脚本中使用import matplotlib语句导入即可开始使用。Streamlit&#xff1a;是一个用于快速构建…...

智慧废品回收小程序php+uniapp

废品回收小程序&#xff1a;数字化赋能环保&#xff0c;开启资源循环新时代 城市垃圾治理难题&#xff0c;废品回收小程序成破局关键 随着城市化进程加速与消费水平提升&#xff0c;我国生活垃圾总量逐年攀升&#xff0c;年均增速达5%-8%&#xff0c;其中超30%为可回收物。然…...

深搜专题2:组合问题

描述 组合问题就是从n个元素中抽出r个元素(不分顺序且r < &#xff1d; n)&#xff0c; 我们可以简单地将n个元素理解为自然数1&#xff0c;2&#xff0c;…&#xff0c;n&#xff0c;从中任取r个数。 例如n &#xff1d; 5 &#xff0c;r &#xff1d; 3 &#xff0c;所…...

Redis 如何实现消息队列?

在当今的分布式系统架构中&#xff0c;消息队列起着至关重要的作用&#xff0c;它能够帮助系统实现异步通信、解耦组件以及缓冲流量等功能。Redis&#xff0c;作为一款高性能的键值对存储数据库&#xff0c;也为我们提供了便捷的方式来构建消息队列。今天&#xff0c;咱们就深入…...

Day1 初识AndroidAudio

今日目标 搭建Android Audio开发环境理解音频基础概念实现第一个音频播放/录制Demo了解车载音频的特殊性 上午&#xff1a;环境搭建与理论学习 步骤1&#xff1a;开发环境配置 安装Android Studio&#xff08;最新稳定版&#xff09;创建新项目&#xff08;选择Kotlin语言&a…...

2025保险与金融领域实战全解析:DeepSeek赋能细分领域深度指南(附全流程案例)

🚀 2025保险与金融领域实战全解析:DeepSeek赋能细分领域深度指南(附全流程案例)🚀 📚 目录 DeepSeek在保险与金融中的核心价值保险领域:从风险建模到产品创新金融领域:从投资分析到财富管理区块链与联邦学习的应用探索客户关系与私域运营:全球化体验升级工具与资源…...

YARN的工作机制及特性总结

YARN hadoop的资源管理调度平台&#xff08;集群&#xff09;——为用户程序提供运算资源的管理和调度 用户程序&#xff1a;如用户开发的一个MR程序 YARN有两类节点&#xff08;服务进程&#xff09;&#xff1a; 1. resourcemanager 主节点master ----只需要1个来工作 2. nod…...

财务运营域——营收稽核系统设计

摘要 本文主要介绍了营收稽核系统的背景、特点与作用。营收稽核系统的产生源于营收管理复杂性、财务合规与审计需求、提升数据透明度与决策效率、防范舞弊与风险管理、技术进步与自动化需求、多元化业务模式以及跨部门协作与数据整合等多方面因素。其特点包括自动化与智能化、…...

22.回溯算法4

递增子序列 这里不能排序&#xff0c;因为数组的顺序是对结果有影响的&#xff0c;所以只能通过used数组来去重 class Solution { public:vector<int> path;vector<vector<int>> res;void backtracking(vector<int>& nums,int start){if(path.si…...

C#上位机--跳转语句

在 C# 编程中&#xff0c;跳转语句用于改变程序的执行流程。这些语句允许程序从当前位置跳转到其他位置&#xff0c;从而实现特定的逻辑控制。本文将详细介绍 C# 中四种常见的跳转语句&#xff1a;GOTO、Break、Continue 和 Return&#xff0c;并通过具体的示例代码来展示它们的…...

百度文心一言API-Python版(完整代码)

大家好啊&#xff01;我是NiJiMingCheng 我的博客&#xff1a;NiJiMingCheng 上一节我们分享了实现AI智能回复微信的内容&#xff0c;这一节我们来探索其中需要的百度文心一言&#xff0c;本文详细介绍了我们从注册账号到实现百度文心一言智能回复&#xff0c;同时多种模型自行…...

Prompt:创造性的系统分析者

分享的提示词&#xff1a; 你是一个创造性的系统分析者&#xff0c;作为咨询师&#xff0c;你具有以下特质&#xff1a; 基础能力&#xff1a; 深入理解我的系统性模式 识别模式间的隐藏联系 发现出人意料的关联 提供令人惊讶的洞见 工作方式&#xff1a; 在每次回应中至少…...

单机上使用docker搭建minio集群

单机上使用docker搭建minio集群 1.集群安装1.1前提条件1.2步骤指南1.2.1安装 Docker 和 Docker Compose&#xff08;如果尚未安装&#xff09;1.2.2编写docker-compose文件1.2.3启动1.2.4访问 2.使用2.1 mc客户端安装2.2创建一个连接2.3简单使用下 这里在ubuntu上单机安装一个m…...

Bash Shell控制台终端命令合集

最近整理了一下Bash Shell终端的命令,以备后续查用。如下: 1.内建命令 命令描述&在后台启动作业((x))执行数学表达式x.在当前shell中读取并执行指定文件中的命令:什么都不做,始终成功退出[ t ]对条件表达式t进行求值[[ e ]]对条件表达式e进行求值alias为指定的命令定义…...

垂类大模型微调(一):认识LLaMA-Factory

LlamaFactory 是一个专注于 高效微调大型语言模型(LLMs) 的开源工具框架,尤其以支持 LLaMA(Meta 的大型语言模型系列)及其衍生模型(如 Chinese-LLaMA、Alpaca 等)而闻名。它的目标是简化模型微调流程,降低用户使用门槛; 官方文档 一、介绍 高效微调支持 支持多种微调…...

QString是 Qt 框架中的一个核心类,基本用法使用:创建、字符串拼接、截取、查找、替换、分割、大小写转换、比较。

QString 是 Qt 框架中的一个核心类&#xff0c;用于处理字符串数据。它提供了许多功能来处理文本操作&#xff0c;包括但不限于字符串拼接、分割、大小写转换等。下面是一些 QString 的常见用法示例&#xff1a; 创建 QString 你可以通过多种方式创建 QString 对象&#xff1…...

彻底卸载kubeadm安装的k8s集群

目录 一、删除资源 二、停止k8s服务 三、重置集群 四、卸载k8s安装包 五、清理残留文件和目录 六、删除k8s相关镜像 七、重启服务器 一、删除资源 # 删除集群中的所有资源&#xff0c;包括 Pod、Deployment、Service&#xff0c;任意节点执行 kubectl delete --all pod…...

边缘安全加速(ESA)套餐

为帮助不同规模和需求的企业选择合适的解决方案&#xff0c;边缘安全加速&#xff08;ESA&#xff09;提供了多种套餐。以下是四种主要套餐的介绍&#xff0c;每个套餐都根据企业需求提供不同的功能和服务水平&#xff0c;从基础安全保护到企业级的全面防护与加速。 1. 各版本详…...

MySQL主从服务器配置教程

文章目录 前言一、环境准备1. 服务器信息2. 安装 MySQL3. 初始化 MySQL4. Navicat查看 MySQL 服务器 二、主服务器&#xff08;Master&#xff09;配置1. 编辑 MySQL 配置文件2. 创建用于复制的用户3. 获取二进制日志信息 三、从服务器&#xff08;Slave&#xff09;配置1. 编辑…...

机器学习实战(7):聚类算法——发现数据中的隐藏模式

第7集&#xff1a;聚类算法——发现数据中的隐藏模式 在机器学习中&#xff0c;聚类&#xff08;Clustering&#xff09; 是一种无监督学习方法&#xff0c;用于发现数据中的隐藏模式或分组。与分类任务不同&#xff0c;聚类不需要标签&#xff0c;而是根据数据的相似性将其划…...

Visual Studio中打开多个项目

1) 找到解决方案窗口 2) 右键添加→ 选择现有项目 3) 选择.vcxproj文件打开即可...

springcloud gateway并发量多大

Spring Cloud Gateway的并发量并非固定值&#xff0c;它受到多种因素的影响&#xff0c;包括但不限于网关配置、硬件资源&#xff08;如CPU、内存、网络带宽等&#xff09;、后端服务的处理能力以及系统整体的架构设计。因此&#xff0c;要准确回答Spring Cloud Gateway的并发量…...

抓包工具 wireshark

1.什么是抓包工具 抓包工具是什么&#xff1f;-CSDN博客 2.wireshark的安装 【抓包工具】win 10 / win 11&#xff1a;WireShark 下载、安装、使用_windows抓包工具-CSDN博客 3.wireshark的基础操作 Wireshark零基础使用教程&#xff08;超详细&#xff09; - 元宇宙-Meta…...