深入分析mysql 6.0.6 和 activiti 6.0.0自动创建表失败的问题

3 篇文章 0 订阅
1 篇文章 0 订阅

问题描述:使用的是mysql数据库,有两个数据库,一个dev,一个test。两个数据库除了数据库名不一样,其他信息均一样。activiti表的生成策略是,如果没有创建,有则更新。当dev或test数据库均未有activiti表时,连接任意一个均能成功,当任意一个有activiti表时,连接另外一个库,启动均报错,比如,dev有activiti表,连接test库,则错误信息如下:


  
  
  1. ### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Table 'test.ACT_GE_PROPERTY' doesn't exist
  2. ### The error may exist in org/activiti/db/mapping/entity/ Property.xml
  3. ### The error may involve org.activiti.engine.impl.persistence.entity.PropertyEntityImpl.selectProperty-Inline
  4. ### The error occurred while setting parameters
  5. ### SQL: select * from ACT_GE_PROPERTY where NAME_ = ?
  6. ### Cause: java.sql.SQLSyntaxErrorException: Table 'test.ACT_GE_PROPERTY' doesn't exist

问题分析:从错误上来看,是activiti查询的时候,"串库"了,认为test库里已经有activiti的表了,所以会执行查询操作。而实际上这些表是在dev库里。为什么会出现这样的情况呢,让我们深入activiti的源码看看吧。以下是activiti 6.0 DBSqlSession的更新逻辑。代码


  
  
  1. public String dbSchemaUpdate() {
  2. String feedback = null;
  3. boolean isUpgradeNeeded = false;
  4. int matchingVersionIndex = - 1;
  5. if ( this.isEngineTablePresent()) { //判断的核心方法,看是否需要更新或创建
  6. //更新操作,省略
  7. } else {
  8. this.dbSchemaCreateEngine();
  9. }
  10. return feedback;
  11. }

  
  
  1. public boolean isEngineTablePresent() {
  2. return this.isTablePresent( "ACT_RU_EXECUTION"); //通过act_ru_execution判断是否存在activiti表
  3. }

  
  
  1. public boolean isTablePresent(String tableName) {
  2. //省略
  3. boolean var8;
  4. try {
  5. //getTables是通过jdbc来实现的,这里的版本是mysql6.0.6
  6. tables = databaseMetaData.getTables(catalog, schema, tableName, JDBC_METADATA_TABLE_TYPES);
  7. var8 = tables.next();
  8. } finally {
  9. try {
  10. tables.close();
  11. } catch (Exception var16) {
  12. log.error( "Error closing meta data tables", var16);
  13. }
  14. }
  15. return var8;
  16. }

从上可以看出,核心关键在于getTables方法,在mysql 6.0.6方法中的getTables核心逻辑代码如下:


  
  
  1. public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, final String[] types) throws SQLException {
  2. try {
  3. //以下是mysql 6.0.6部分源码逻辑,感兴趣的朋友可以自己去看完整源码
  4. final SortedMap<DatabaseMetaData.TableMetaDataKey, Row> sortedRows = new TreeMap();
  5. ArrayList<Row> tuples = new ArrayList();
  6. final Statement stmt = this.conn.getMetadataSafeStatement();
  7. String tmpCat = "";
  8. if (catalog != null && catalog.length() != 0) {
  9. tmpCat = catalog;
  10. } else if ( this.nullCatalogMeansCurrent) {
  11. //问题的关键点,this.nullCatalogMeansCurrent为false,导致tmpCat为空,tmpCat理应是数据库名
  12. //而nullCatalogMeansCurrent在6.0.6中默认为false,赋值是在构造函数里赋的值
  13. tmpCat = this.database;
  14. }
  15. List<String> parseList = StringUtils.splitDBdotName(tableNamePattern, tmpCat, this.quotedId, this.conn.isNoBackslashEscapesSet());
  16. final String tableNamePat;
  17. if (parseList.size() == 2) {
  18. tableNamePat = (String)parseList. get( 1);
  19. } else {
  20. tableNamePat = tableNamePattern;
  21. }
  22. try {
  23. (new IterateBlock<String>( this.getCatalogIterator(catalog)) {
  24. // catalogStr 是数据库名,如果nullCatalogMeansCurrent为false,则遍历所有的数据库链接
  25. // 如果nullCatalogMeansCurrent为true,则使用指定的数据库名,不遍历。
  26. void forEach(String catalogStr) throws SQLException {
  27. //排除information_schema, mysql 和 performance_schema 系统DB
  28. boolean operatingOnSystemDB = "information_schema".equalsIgnoreCase(catalogStr) || "mysql".equalsIgnoreCase(catalogStr) || "performance_schema".equalsIgnoreCase(catalogStr);
  29. ResultSet results = null;
  30. try {
  31. results = stmt.executeQuery( "SHOW FULL TABLES FROM " + StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData. this.quotedId, DatabaseMetaData. this.pedantic) + " LIKE " + StringUtils.quoteIdentifier(tableNamePat, "'", true));
  32. } catch (SQLException var27) {
  33. if (! "08S01".equals(var27.getSQLState())) {
  34. return;
  35. }
  36. //doForAll(),遍历数据库链接,如果nullCatalogMeansCurrent为true,不遍历。
  37. }).doForAll();
  38. } finally {
  39. if (stmt != null) {
  40. stmt.close();
  41. }
  42. }
  43. tuples.addAll(sortedRows.values());
  44. ResultSet tables = this.resultSetFactory.createFromResultsetRows( 1007, 1004, new ResultsetRowsStatic(tuples, this.createTablesFields()));
  45. return tables;
  46. } catch (CJException var16) {
  47. throw SQLExceptionsMapping.translateException(var16, this.getExceptionInterceptor());
  48. }
  49. }

从上面源码可以看出,当nullCatalogMeansCurrent为false时,mysql驱动会遍历当前链接的所有表,执行以下语句,判断是否存在Activiti表。这样就会出现文章开头所讲的“串库”的效果。

SHOW FULL TABLES FROM `TABLE_NAME` LIKE 'ACT_RU_EXECUTION'
  
  

如果nullCatalogMeansCurrent为true,则使用指定的数据库来执行查询语句,不会遍历。再来看看nullCatalogMeansCurrent的赋值逻辑:


  
  
  1. protected DatabaseMetaData(JdbcConnection connToSet, String databaseToSet, ResultSetFactory resultSetFactory) {
  2. //省略
  3. this.nullCatalogMeansCurrent = (( Boolean) this.conn.getPropertySet().getBooleanReadableProperty( "nullCatalogMeansCurrent").getValue()).booleanValue();
  4. }

可以看出是从property里取的值,也就是url地址栏后面,也就是说在地址栏nullCatalogMeansCurrent=true即可。

另外,我又试了下降低mysql的版本,改成5.1.46,发现没有问题,看了一下它的实现代码,发现nullCatalogMeansCurrent属性默认为true,在ConnectionPropertiesImpl类的构造函数里赋的值,感兴趣的朋友可以自行去查看。代码逻辑如下:


  
  
  1. public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, final String[] types) throws SQLException {
  2. //这里只罗列部分代码
  3. final SortedMap<DatabaseMetaData.TableMetaDataKey, ResultSetRow> sortedRows = new TreeMap();
  4. ArrayList<ResultSetRow> tuples = new ArrayList();
  5. final Statement stmt = this.conn.getMetadataSafeStatement();
  6. String tmpCat = "";
  7. if (catalog != null && catalog.length() != 0) {
  8. tmpCat = catalog;
  9. } else if ( this.conn.getNullCatalogMeansCurrent()) {
  10. //从Connection里面获取的nullCatalogMeansCurrent
  11. //这里的赋值操作是ConnectionPropertiesImpl,默认赋的是true。
  12. tmpCat = this.database;
  13. }
  14. tuples.addAll(sortedRows.values());
  15. ResultSet tables = this.buildResultSet( this.createTablesFields(), tuples);
  16. return tables;
  17. }

问题总结:

1. 从mysql-connector-java 5.x 到 6.x,nullCatalogMeansCurrent属性由原来的默认true改为了false。

2. true 使用指定的数据库进行查询。优先取当前传入的数据库名,其次取当前链接的数据库名。

3. false 代表遍历当前链接下的所有数据库进行查询,官网说的是按照目录查询,说实话,我刚开始看到这官方说法,我是黑人问号脸的。后来通过调试源码才明白,其实就是遍历当前链接下的所有数据库(information_schema, mysql 和 performance_schema 这三个系统DB,虽然在最终结果里进行了排除,但是依然进行了查询,还因此做了很多逻辑处理,这块感觉可以进行优化,比如可以另加一个参数,只遍历自定义的库等等),这也解释了一开始遇到的“串库”问题。

解决方法:

1. 将mysql版本降为5.x

2. 在url后面加上nullCatalogMeansCurrent=true

既然选择了新版本,那就用第二种吧。



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值