一個MySql例項自動建立多個Activiti資料庫問題
一次使用SSH和Activiti6開發的專案中,在伺服器啟動的時候就報錯:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Table 'activiti.act_ge_property' doesn't exist
### The error may exist in org/activiti/db/mapping/entity/Property.xml
### The error may involve org.activiti.engine.impl.persistence.entity.PropertyEntityImpl.selectProperty-Inline
### The error occurred while setting parameters
### SQL: select * from ACT_GE_PROPERTY where NAME_ = ?
### Cause: java.sql.SQLSyntaxErrorException: Table 'activiti.act_ge_property' doesn't exist
查詢表不存在?Activiti沒有自動建表?
可配置檔案確實將databaseSchemaUpdate屬性設定為了true,這意味著activiti會執行無表建立,有表更新的策略
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="pooledDataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchema" value="roomRent" />
<property name="databaseSchemaUpdate" value="true" />
<property name="asyncExecutorActivate" value="false" />
</bean>
但執行顯示並沒有起效。網上也沒有靠譜的結果。。
只能自己檢視原始碼。databaseSchemaUpdate設為true的時候,引擎會根據該值執行不同的策略,
public void performSchemaOperationsProcessEngineBuild() { String databaseSchemaUpdate = Context.getProcessEngineConfiguration().getDatabaseSchemaUpdate(); log.debug("Executing performSchemaOperationsProcessEngineBuild with setting " + databaseSchemaUpdate); if (ProcessEngineConfigurationImpl.DB_SCHEMA_UPDATE_DROP_CREATE.equals(databaseSchemaUpdate)) { try { dbSchemaDrop(); } catch (RuntimeException e) { // ignore } } if (org.activiti.engine.ProcessEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP.equals(databaseSchemaUpdate) || ProcessEngineConfigurationImpl.DB_SCHEMA_UPDATE_DROP_CREATE.equals(databaseSchemaUpdate) || ProcessEngineConfigurationImpl.DB_SCHEMA_UPDATE_CREATE.equals(databaseSchemaUpdate)) { dbSchemaCreate(); } else if (org.activiti.engine.ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE.equals(databaseSchemaUpdate)) { dbSchemaCheckVersion(); } else if (ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE.equals(databaseSchemaUpdate)) { dbSchemaUpdate(); } }
它會根據databaseSchemaUpdate的值執行對應的方法,設為true則執行:
dbSchemaUpdate();
該方法中首先會判斷引擎的表是否已存在了,存在執行更新邏輯,不存在執行建立邏輯
public String dbSchemaUpdate() {
String feedback = null;
boolean isUpgradeNeeded = false;
int matchingVersionIndex = -1;
if (isEngineTablePresent()) {
PropertyEntity dbVersionProperty = selectById(PropertyEntity.class, "schema.version");
String dbVersion = dbVersionProperty.getValue();
......
}else {
dbSchemaCreateEngine();
}
問題就在這裡,判斷存在的時候會查詢act_ge_property表中的
schema.version
欄位,但這個時候你的資料庫中是沒有自動建立activiti的任何表的,所以會報出上面的異常。
那為什麼
isEngineTablePresent()
方法會判斷為true呢?
public boolean isEngineTablePresent() {
return isTablePresent("ACT_RU_EXECUTION");通過判斷表ACT_RU_EXECUTION是否存在
}
public boolean isTablePresent(String tableName) {
// ACT-1610: in case the prefix IS the schema itself, we don't add the
// prefix, since the check is already aware of the schema
if (!dbSqlSessionFactory.isTablePrefixIsSchema()) {
tableName = prependDatabaseTablePrefix(tableName);
}
Connection connection = null;
try {
connection = sqlSession.getConnection();
DatabaseMetaData databaseMetaData = connection.getMetaData();
ResultSet tables = null;
String catalog = this.connectionMetadataDefaultCatalog;
if (dbSqlSessionFactory.getDatabaseCatalog() != null && dbSqlSessionFactory.getDatabaseCatalog().length() > 0) {
catalog = dbSqlSessionFactory.getDatabaseCatalog();
}
String schema = this.connectionMetadataDefaultSchema;
if (dbSqlSessionFactory.getDatabaseSchema() != null && dbSqlSessionFactory.getDatabaseSchema().length() > 0) {
schema = dbSqlSessionFactory.getDatabaseSchema();
}
String databaseType = dbSqlSessionFactory.getDatabaseType();
if ("postgres".equals(databaseType)) {
tableName = tableName.toLowerCase();
}
if (schema != null && "oracle".equals(databaseType)) {
schema = schema.toUpperCase();
}
if (catalog != null && catalog.length() == 0) {
catalog = null;
}
try {
tables = databaseMetaData.getTables(catalog, schema, tableName, JDBC_METADATA_TABLE_TYPES);
return tables.next();
} finally {
try {
tables.close();
} catch (Exception e) {
log.error("Error closing meta data tables", e);
}
}
} catch (Exception e) {
throw new ActivitiException("couldn't check if tables are already present using metadata: " + e.getMessage(), e);
}
}
再以下的程式碼就是c3p0和mysql驅動那的了,我找了很久也沒完全理清,而且maven幫我下的c3p0的原始碼和專案用的還不一樣(為什麼?。。),限於時間就沒繼續了,這裡也不貼出來了。
最終方法判斷為true的原因其實就是判斷表
ACT_RU_EXECUTION
是否存在的時候找到了之前另一個專案的activiti資料庫中的表,因而報錯,把那個資料庫刪了,就執行通過了。有興趣的同學可以自己看下後面執行的程式碼,應該是有一個快速判斷資料庫中某個表是否存在的方法。
上面配置檔案中我加了一個scheme屬性
<property name="databaseSchema" value="roomRent" />
值是本專案存放activiti資料表的資料庫名(異常資訊中是為了再現該問題用另一個專案做的測試,所以兩個資料庫不一樣),本意是期待spring能夠像注入其它屬性一樣把該屬性也注入進activiti的配置類中,然後查詢資料表是否存在的時候加上scheme約束,但除錯的時候發現這個值沒注入進去。並且正常情況下加入這個屬性c3p0那邊會報錯,所以還是得去掉。
history和id的表第一次啟動專案的時候並沒有建立,它們會在之後比如第二次啟動專案或者用到的時候自動建立。
當然這個問題還有別的解決方案,比如手動呼叫activiti提供的建表指令碼建立等,相關教程可以很容易搜到。