Activiti 数据库表结构大小写转换(springMVC&springBoot)

tech2023-11-28  74

Activiti 数据库表结构大小写转换

背景实操修改数据库服务利用拦截器处理拦截器使用通过源码窥探实现方案 DbSqlSessionFactory&sqlSessionFactory

背景

项目中使用了activiti,activiti使用mysql作为数据源。上线时被DBA卡住了,原因是大写的表结构不符合sql规范,一番沟通后无果,无奈只能自己想办法解决了。经过一番探索,最终成功解决表结构大写问题,现总结如下。

注:首先你肯定需要自己将建表语句改为小写,然后创建对应的表,这里需要注意的一点是

act_ge_property

需要有初始化数据,否则项目无法正常启动。

实操

修改数据库服务

这个是最直接也最简单的方式,直接修改数据库服务,使其大小写不敏感。

打开my.cnf文件,加入以下语句后重启。 lower_case_table_names = 1

但是此方法受是否可操作服务器的局限。很明显,这个方法在我这这里行不通。

利用拦截器处理

使用mybatis的拦截器在向数据库发送sql的时候进行拦截,将其转换为小写的语句。

拦截器使用

定义拦截器 xxInterceptor 使其实现 mybatis的 Interceptor接口;在mybaits的全局配置文件中,声明这个插件。 @Intercepts({ @Signature( type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class }) }) @Component public class MySqlInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); //获取到原始sql语句 String sql = boundSql.getSql(); sql = convertTableNameToLower(sql); Field field = boundSql.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(boundSql, sql); return invocation.proceed(); } @Override public Object plugin(Object o) { return Plugin.wrap(o, this); } private String convertTableNameToLower(String sql) { //TODO 对sql进行处理 return sql; } @Override public void setProperties(Properties properties) { } } <!-- MyBatis全局配置文件:mybatis-config.xml --> <plugins> <plugin interceptor="xxx.xxx.xxx.xxx.MySqlInterceptor"> <!--此处可以定义一些用得着的属性--> <property name="propertyXXX" value="valueXXX"/> </plugin> </plugins>

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内,至此,改造结束。

DbSqlSessionFactory&sqlSessionFactory

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); }
最新回复(0)