1. 程式人生 > >一個MySql例項自動建立多個Activiti資料庫問題

一個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提供的建表指令碼建立等,相關教程可以很容易搜到。