一次使用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的时候,引擎会根据该值执行不同的策略,
在类org.activiti.engine.impl.db.DbSqlSession中,有这样一个方法:
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提供的建表脚本创建等,相关教程可以很容易搜到。