一、项目启动问题
Caused by: org.activiti.engine.ActivitiException: couldn't deduct database type from database product name 'DM DBMS'
at org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl.initDatabaseType(ProcessEngineConfigurationImpl.java:1133)
at org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl.initDataSource(ProcessEngineConfigurationImpl.java:1077)
at org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl.init(ProcessEngineConfigurationImpl.java:864)
at org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl.buildProcessEngine(ProcessEngineConfigurationImpl.java:847)
at org.activiti.spring.SpringProcessEngineConfiguration.buildProcessEngine(SpringProcessEngineConfiguration.java:79)
at org.activiti.spring.ProcessEngineFactoryBean.getObject(ProcessEngineFactoryBean.java:57)
at org.activiti.spring.ProcessEngineFactoryBean.getObject(ProcessEngineFactoryBean.java:32)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171)
... 89 common frames omitted
● 由上述错误信息表明 Activiti 引擎无法从数据库产品名称“DM DBMS
”中识别出数据库类型。
● 通过查看源码,我们发现activiti并没有对达梦数据库进行集成,那怎么办呢?只能是通过修改源码的方式将达梦数据库集成到activiti当中。
二、问题分析
// 根据报错日志信息找源码,排查问题在何处抛出异常
// 报错信息
at org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl.buildProcessEngine(ProcessEngineConfigurationImpl.java:847)
// 源码
public ProcessEngine buildProcessEngine() {
// 初始化 ProcessEngineConfiguration,可能包括设置各种属性、加载配置文件或执行其他必要的初始化操作
init();
ProcessEngineImpl processEngine = new ProcessEngineImpl(this);
// 该方法用于在 ProcessEngine 初始化完成后进行一些后续处理,可能包括注册事件监听器、执行自定义扩展等操作。
postProcessEngineInitialisation();
return processEngine;
}
// 报错信息
at org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl.init(ProcessEngineConfigurationImpl.java:864)
// 源码
public void init() {
// 该方法用于初始化配置器(Configurator),可能包括注册和设置各种配置器的逻辑处理。
initConfigurators();
// 该方法用于在初始化之前执行一些额外的配置器逻辑。
configuratorsBeforeInit();
// 该方法用于初始化历史记录级别,可能包括设置历史记录级别的属性或初始化相关的历史记录级别对象
initHistoryLevel();
// 用于初始化表达式管理器,可能涉及设置相关属性或初始化表达式管理器的逻辑
initExpressionManager();
if (usingRelationalDatabase) {//是否为关系型数据库
// 该方法用于初始化数据源,可能包括设置数据源的属性、建立数据库连接等操作。
initDataSource();
}
…………
}
// 报错信息
at org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl.initDataSource(ProcessEngineConfigurationImpl.java:1077)
// 源码
public void initDataSource() {
if (dataSource == null) {
// 通过 JNDI 查找数据源并将其赋值给 dataSource 变量。如果查找失败,则抛出异常
if (dataSourceJndiName != null) {
try {
dataSource = (DataSource) new InitialContext().lookup(dataSourceJndiName);
} catch (Exception e) {
throw new ActivitiException("couldn't lookup datasource from " + dataSourceJndiName + ": " + e.getMessage(), e);
}
} else if (jdbcUrl != null) {
// 没有同时设置 jdbcDriver 和 jdbcUsername,则抛出异常,要求在流程引擎配置中指定数据源或 JDBC 属性。
if ((jdbcDriver == null) || (jdbcUsername == null)) {
throw new ActivitiException("DataSource or JDBC properties have to be specified in a process engine configuration");
}
log.debug("initializing datasource to db: {}", jdbcUrl);
// 初始化一个 PooledDataSource 对象,并使用指定的参数(jdbcDriver、jdbcUrl、jdbcUsername、jdbcPassword)配置数据源。
PooledDataSource pooledDataSource = new PooledDataSource(ReflectUtil.getClassLoader(), jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword);
// 根据配置的属性设置数据源的连接池参数,包括最大活动连接数、最大空闲连接数、最大连接等待时间、连接最大空闲时间。
if (jdbcMaxActiveConnections > 0) {
pooledDataSource.setPoolMaximumActiveConnections(jdbcMaxActiveConnections);
}
if (jdbcMaxIdleConnections > 0) {
pooledDataSource.setPoolMaximumIdleConnections(jdbcMaxIdleConnections);
}
if (jdbcMaxCheckoutTime > 0) {
pooledDataSource.setPoolMaximumCheckoutTime(jdbcMaxCheckoutTime);
}
if (jdbcMaxWaitTime > 0) {
pooledDataSource.setPoolTimeToWait(jdbcMaxWaitTime);
}
// 如果启用了连接池的心跳检测(jdbcPingEnabled),设置相关的心跳查询和连接未使用时间的阈值。
if (jdbcPingEnabled) {
pooledDataSource.setPoolPingEnabled(true);
if (jdbcPingQuery != null) {
pooledDataSource.setPoolPingQuery(jdbcPingQuery);
}
pooledDataSource.setPoolPingConnectionsNotUsedFor(jdbcPingConnectionNotUsedFor);
}
// 如果配置了默认的事务隔离级别(jdbcDefaultTransactionIsolationLevel),设置数据源的默认事务隔离级别。
if (jdbcDefaultTransactionIsolationLevel > 0) {
pooledDataSource.setDefaultTransactionIsolationLevel(jdbcDefaultTransactionIsolationLevel);
}
dataSource = pooledDataSource;
}
// 如果 dataSource 是 PooledDataSource 的实例,强制关闭所有连接。这里是为了解决 iBatis 连接池初始化的问题
if (dataSource instanceof PooledDataSource) {
// ACT-233: connection pool of Ibatis is not properly
// initialized if this is not called!
((PooledDataSource) dataSource).forceCloseAll();
}
}
// 如果 databaseType 为空,则调用 initDatabaseType() 方法来初始化数据库类型
if (databaseType == null) {
initDatabaseType();
}
}
// 报错信息
at org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl.initDatabaseType(ProcessEngineConfigurationImpl.java:1133)
// 源码
public void initDatabaseType() {
Connection connection = null;
try {
// 创建一个数据库连接对象 connection,并通过 dataSource.getConnection() 方法从数据源中获取一个数据库连接
connection = dataSource.getConnection();
// 获取数据库的元数据信息,包括数据库产品名称。
DatabaseMetaData databaseMetaData = connection.getMetaData();
// 方法获取数据库产品名称
String databaseProductName = databaseMetaData.getDatabaseProductName();
log.debug("database product name: '{}'", databaseProductName);
// 在配置的 databaseTypeMappings 属性中查找对应的数据库类型,并将结果赋值给变量 databaseType
databaseType = databaseTypeMappings.getProperty(databaseProductName);
if (databaseType == null) {
// 抛出异常信息为:couldn't deduct database type from database product name 'DM DBMS'
throw new ActivitiException("couldn't deduct database type from database product name '" + databaseProductName + "'");
}
} catch (SQLException e) {
log.error("Exception while initializing Database connection", e);
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
log.error("Exception while closing the Database connection", e);
}
}
}
● 由上述报错日志信息和源码分析可以发现,databaseType = databaseTypeMappings.getProperty(databaseProductName)
值为空,则表明在databaseTypeMappings
属性中没有查找【达梦】
相关数据库信息。查找databaseTypeMapping
配置属性相关代码,如下:
protected static Properties databaseTypeMappings = getDefaultDatabaseTypeMappings();
public static final String DATABASE_TYPE_H2 = "h2";
public static final String DATABASE_TYPE_HSQL = "hsql";
public static final String DATABASE_TYPE_MYSQL = "mysql";
public static final String DATABASE_TYPE_ORACLE = "oracle";
public static final String DATABASE_TYPE_POSTGRES = "postgres";
public static final String DATABASE_TYPE_MSSQL = "mssql";
public static final String DATABASE_TYPE_DB2 = "db2";
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("Oracle", DATABASE_TYPE_ORACLE);
databaseTypeMappings.setProperty("PostgreSQL", 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);
………………
return databaseTypeMappings;
}
● 该配置中并未对代码数据集成,因此我们需要去修改源代码将【达梦数据库】进行集成进来。
● 解决方案:下载activiti-engine-7.1.0.jar
文件,通过反编译工具luyten
对其进行反编译得到class文件,修改源码对达梦进行集成,在打成jar
包。
三、集成达梦数据库
● 修改ProcessEngineConfigurationImpl.java
文件如下:
// 添加成员变量以适配达梦数据库
public static final String DATABASE_TYPE_DM = "dm";//达梦
public static final String DATABASE_TYPE_H2 = "h2";
public static final String DATABASE_TYPE_HSQL = "hsql";
………………
public static Properties getDefaultDatabaseTypeMappings() {
Properties databaseTypeMappings = new Properties();
// 适配达梦数据库
databaseTypeMappings.setProperty("DM DBMS", DATABASE_TYPE_DM);
databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2);
databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL);
……………………
}
//修改initSqlSessionFactory()方法
if (databaseType != null) {
if (DATABASE_TYPE_DM.equals(databaseType)){
properties.load(getResourceAsStream("org/activiti/db/properties/" + DATABASE_TYPE_ORACLE + ".properties"));
}else {
properties.load(getResourceAsStream("org/activiti/db/properties/" + databaseType + ".properties"));
}
}
● 修改DbSqlSessionFactory.java
文件下的initBulkInsertEnabledMap(String databaseType)
方法,修改如下:
protected void initBulkInsertEnabledMap(String databaseType) {
bulkInsertableMap = new HashMap<Class<? extends Entity>, Boolean>();
for (Class<? extends Entity> clazz : EntityDependencyOrder.INSERT_ORDER) {
bulkInsertableMap.put(clazz, Boolean.TRUE);
}
// Only Oracle is making a fuss in one specific case right now
//此处添加达梦数据库类型
// 适配达梦数据库
if ("oracle".equals(databaseType) || "dm".equals(databaseType)) {
bulkInsertableMap.put(EventLogEntryEntityImpl.class, Boolean.FALSE);
}
}
● 修改AbstractQuery.java
文件中的addOrder(String column, String sortOrder, NullHandlingOnOrder nullHandlingOnOrder)
方法,修改如下:
if (nullHandlingOnOrder.equals(NullHandlingOnOrder.NULLS_FIRST)) {
// 适配达梦数据库类型
if (ProcessEngineConfigurationImpl.DATABASE_TYPE_H2.equals(databaseType) || ProcessEngineConfigurationImpl.DATABASE_TYPE_HSQL.equals(databaseType)
|| ProcessEngineConfigurationImpl.DATABASE_TYPE_POSTGRES.equals(databaseType) || ProcessEngineConfigurationImpl.DATABASE_TYPE_ORACLE.equals(databaseType) || ProcessEngineConfigurationImpl.DATABASE_TYPE_DM.equals(databaseType)) {
orderBy = orderBy + defaultOrderByClause + " NULLS FIRST";
} else if (ProcessEngineConfigurationImpl.DATABASE_TYPE_MYSQL.equals(databaseType)) {
orderBy = orderBy + "isnull(" + column + ") desc," + defaultOrderByClause;
} else if (ProcessEngineConfigurationImpl.DATABASE_TYPE_DB2.equals(databaseType) || ProcessEngineConfigurationImpl.DATABASE_TYPE_MSSQL.equals(databaseType)) {
orderBy = orderBy + "case when " + column + " is null then 0 else 1 end," + defaultOrderByClause;
} else {
orderBy = orderBy + defaultOrderByClause;
}
} else if (nullHandlingOnOrder.equals(NullHandlingOnOrder.NULLS_LAST)) {
// 适配达梦数据库类型
if (ProcessEngineConfigurationImpl.DATABASE_TYPE_H2.equals(databaseType) || ProcessEngineConfigurationImpl.DATABASE_TYPE_HSQL.equals(databaseType)
|| ProcessEngineConfigurationImpl.DATABASE_TYPE_POSTGRES.equals(databaseType) || ProcessEngineConfigurationImpl.DATABASE_TYPE_ORACLE.equals(databaseType) || ProcessEngineConfigurationImpl.DATABASE_TYPE_DM.equals(databaseType)) {
orderBy = orderBy + column + " " + sortOrder + " NULLS LAST";
} else if (ProcessEngineConfigurationImpl.DATABASE_TYPE_MYSQL.equals(databaseType)) {
orderBy = orderBy + "isnull(" + column + ") asc," + defaultOrderByClause;
} else if (ProcessEngineConfigurationImpl.DATABASE_TYPE_DB2.equals(databaseType) || ProcessEngineConfigurationImpl.DATABASE_TYPE_MSSQL.equals(databaseType)) {
orderBy = orderBy + "case when " + column + " is null then 1 else 0 end," + defaultOrderByClause;
} else {
orderBy = orderBy + defaultOrderByClause;
}
}