项目中使用了activiti,activiti使用mysql作为数据源。上线时被DBA卡住了,原因是大写的表结构不符合sql规范,一番沟通后无果,无奈只能自己想办法解决了。经过一番探索,最终成功解决表结构大写问题,现总结如下。
注:首先你肯定需要自己将建表语句改为小写,然后创建对应的表,这里需要注意的一点是
act_ge_property需要有初始化数据,否则项目无法正常启动。
这个是最直接也最简单的方式,直接修改数据库服务,使其大小写不敏感。
打开my.cnf文件,加入以下语句后重启。 lower_case_table_names = 1但是此方法受是否可操作服务器的局限。很明显,这个方法在我这这里行不通。
使用mybatis的拦截器在向数据库发送sql的时候进行拦截,将其转换为小写的语句。
OK,此时一个sql拦截器就定义好了,启动项目试试吧。 没错,正如你期待的那样。如果你的表结构此时已经改成小写了,那么在启动的时候项目会报错,告诉你表不存在。但是,到这肯定不甘心啊,我的拦截器到底生效了没,是不是白搞了。于是,我为了项目能起来,做了如下操作
先把表全部干掉,让activiti启动的时候自己创建表,等项目起来以后,再把系统自动创建的大写的表干掉,改成自己定义的小写表。此时再测试一下(在自己定义的拦截器那块加个断点试试),没错,进来了,数据也正常写入了。至少取得了阶段性胜利。
思考:为什么项目启动的时候拦截器未生效,而在正常启动之后可以正常运行? 补充:拦截器的添加是通过sqlSessionFactory添加的
sqlSessionFactory.getConfiguration().addInterceptor(interceptor);猜测是activiti自己对mybatis的SqlSessionFactory做了封装,并且与mybatis的默认DefaultSqlSessionFactory建立了联系。在启动的时候使用自己的SqlSessionFactory,项目启动之后DefaultSqlSessionFactory也初始化完成,此时的DefaultSqlSessionFactory已经加载了自定义的拦截器。自定义的与默认的又建立了联系,所以启动后可以正常使用。
后来的探索也验证了这一点。activiti自己维护了一个DbSqlSessionFactory。 那如果我能在DbSqlSessionFactory这个上面添加我自定义的拦截器,那应该就可以正常使用了。
从这里入手
org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl#init public void init() { this.initConfigurators(); this.configuratorsBeforeInit(); // …… this.configuratorsAfterInit(); }查看initConfigurators方法
public void initConfigurators() { this.allConfigurators = new ArrayList(); //加载默认的configurators if (this.configurators != null) { Iterator var1 = this.configurators.iterator(); while(var1.hasNext()) { ProcessEngineConfigurator configurator = (ProcessEngineConfigurator)var1.next(); this.allConfigurators.add(configurator); } } if (this.enableConfiguratorServiceLoader) { ClassLoader classLoader = this.getClassLoader(); //加载自定义的configurators ServiceLoader<ProcessEngineConfigurator> configuratorServiceLoader = ServiceLoader.load(ProcessEngineConfigurator.class, classLoader); for(var4 = configuratorServiceLoader.iterator(); var4.hasNext(); ++nrOfServiceLoadedConfigurators) { // …… this.allConfigurators.add(configurator); } //…… } }为了查看清晰,代码仅保留了我们需要关注的内容,此处主要做了两件事情
加载默认的configurators添加至allConfigurators加载自定义的configurators添加至allConfigurators加载自定义配置的时候,注意到ProcessEngineConfigurator
public interface ProcessEngineConfigurator { void beforeInit(ProcessEngineConfigurationImpl var1); void configure(ProcessEngineConfigurationImpl var1); int getPriority(); }然后在configuratorsBeforeInit()及configuratorsAfterInit()中分别调用了beforeInit()及configure()方法。
SqlSessionFactory sqlSessionFactory = ProcessEngineConfigurationImpl.getSqlSessionFactory();拿到sqlSessionFactory就好办了。
正如上文 “补充” 中提到,只要通过
sqlSessionFactory.getConfiguration().addInterceptor(interceptor)添加拦截器即可。接下来开始自定义configurators
实现ProcessEngineConfigurator(这里选择extend ProcessEngineConfigurator的抽象类AbstractProcessEngineConfigurator) public class MyProcessEngineConfigurator extends AbstractProcessEngineConfigurator { public void configure(ProcessEngineConfigurationImpl processEngineConfiguration) { SqlSessionFactory sqlSessionFactory = processEngineConfiguration.getSqlSessionFactory(); //这里是自定义的MySqlInterceptor,跟上文一样 sqlSessionFactory.getConfiguration().addInterceptor(new MySqlInterceptor()); } } 添加到配置文件 <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <!-- …… --> <property name="configurators"> <list> <bean class="com.xxx.xxx.MyProcessEngineConfigurator"></bean> </list> </property> </bean>如果是springBoot的项目,直接使用配置类的方式
@Configuration public class MyAbstractProcessEngineConfigurator implements ProcessEngineConfigurationConfigurer { @Override public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) { List<ProcessEngineConfigurator> list = new ArrayList<>(); list.add(new MyProcessEngineConfigurator()); springProcessEngineConfiguration.setConfigurators(list); } }这样,在项目启动的时候,就可以将自定义的拦截器加载进activiti内,至此,改造结束。
activiti自己维护的DbSqlSessionFactory与mybatis的sqlSessionFactory的关系是如何维护的呢?
public void init() { this.initConfigurators(); this.configuratorsBeforeInit(); // …… if (this.usingRelationalDatabase) { this.initSqlSessionFactory(); } this.initSessionFactories(); // …… this.configuratorsAfterInit(); }看到这里initSqlSessionFactory,创建mybatis的DefaultSqlSessionFactory
public void initSqlSessionFactory() { if (this.sqlSessionFactory == null) { InputStream inputStream = null; try { inputStream = this.getMyBatisXmlConfigurationStream(); Environment environment = new Environment("default", this.transactionFactory, this.dataSource); Reader reader = new InputStreamReader(inputStream); Properties properties = new Properties(); properties.put("prefix", this.databaseTablePrefix); String wildcardEscapeClause = ""; if (this.databaseWildcardEscapeCharacter != null && this.databaseWildcardEscapeCharacter.length() != 0) { wildcardEscapeClause = " escape '" + this.databaseWildcardEscapeCharacter + "'"; } properties.put("wildcardEscapeClause", wildcardEscapeClause); properties.put("limitBefore", ""); properties.put("limitAfter", ""); properties.put("limitBetween", ""); properties.put("limitOuterJoinBetween", ""); properties.put("limitBeforeNativeQuery", ""); properties.put("orderBy", "order by ${orderByColumns}"); properties.put("blobType", "BLOB"); properties.put("boolValue", "TRUE"); if (this.databaseType != null) { properties.load(this.getResourceAsStream("org/activiti/db/properties/" + this.databaseType + ".properties")); } Configuration configuration = this.initMybatisConfiguration(environment, reader, properties); this.sqlSessionFactory = new DefaultSqlSessionFactory(configuration); } catch (Exception var10) { throw new ActivitiException("Error while building ibatis SqlSessionFactory: " + var10.getMessage(), var10); } finally { IoUtil.closeSilently(inputStream); } } }然后将DefaultSqlSessionFactory赋给dbSqlSessionFactory
public void initSessionFactories() { if (this.sessionFactories == null) { this.sessionFactories = new HashMap(); if (this.usingRelationalDatabase) { this.initDbSqlSessionFactory(); } this.addSessionFactory(new GenericManagerFactory(EntityCache.class, EntityCacheImpl.class)); } if (this.customSessionFactories != null) { Iterator var1 = this.customSessionFactories.iterator(); while(var1.hasNext()) { SessionFactory sessionFactory = (SessionFactory)var1.next(); this.addSessionFactory(sessionFactory); } } } public void initDbSqlSessionFactory() { if (this.dbSqlSessionFactory == null) { this.dbSqlSessionFactory = this.createDbSqlSessionFactory(); } this.dbSqlSessionFactory.setDatabaseType(this.databaseType); this.dbSqlSessionFactory.setIdGenerator(this.idGenerator); //这里添加 this.dbSqlSessionFactory.setSqlSessionFactory(this.sqlSessionFactory); this.dbSqlSessionFactory.setDbIdentityUsed(this.isDbIdentityUsed); this.dbSqlSessionFactory.setDbHistoryUsed(this.isDbHistoryUsed); this.dbSqlSessionFactory.setDatabaseTablePrefix(this.databaseTablePrefix); this.dbSqlSessionFactory.setTablePrefixIsSchema(this.tablePrefixIsSchema); this.dbSqlSessionFactory.setDatabaseCatalog(this.databaseCatalog); this.dbSqlSessionFactory.setDatabaseSchema(this.databaseSchema); this.dbSqlSessionFactory.setBulkInsertEnabled(this.isBulkInsertEnabled, this.databaseType); this.dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(this.maxNrOfStatementsInBulkInsert); this.addSessionFactory(this.dbSqlSessionFactory); }