背景
在数据库操作过程中,经常有修改表名的需求,例如:分表时,修改表名加上分表前后缀;多数据库一个实例,表名前面加上库名,实现本地跨库事务等等。
我用到的场景是后者,动态加库名。由于使用了MybatisPlus,整个开发过程中不会自己写sql,也没办法通过硬编码修改表名。
解决方案
MybatisPlus中只需要一个配置类即可自定义,动态修改表名,代码如下:
@Configuration
public class MybatisPlusConfig {
List<String> tables = Lists.newArrayList("user","class");
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
dynamicTableNameParser.setTableNameHandlerMap(new HashMap<String, ITableNameHandler>() {{
tables.forEach(tableTitle -> put(tableTitle,(metaObject, sql, tableName) -> "db01." + tableName ));
}});
paginationInterceptor.setSqlParserList(Collections.singletonList(dynamicTableNameParser));
return paginationInterceptor;
}
}
代码说明:
两个核心类:DynamicTableNameParser 动态解析表名,ITableNameHandler 表名处理。
DynamicTableNameParser会根据当前sql的tableName获取对应的ITableNameHandler,上面用拉姆达表达式,实现了ITableNameHandler中的dynamicTableName方法,拼上前缀。
ITableNameHandler源码如下:
/**
* 表名 SQL 处理
*
* @param metaObject 元对象
* @param sql 当前执行 SQL
* @param tableName 表名
* @return
*/
default String process(MetaObject metaObject, String sql, String tableName) {
String dynamicTableName = dynamicTableName(metaObject, sql, tableName);
if (null != dynamicTableName && !dynamicTableName.equalsIgnoreCase(tableName)) {
// 直接替换字符串对于 SQL 操作是不那么好做,这里修复只能尽可能的保证处理没问题
String regex = "(?<=\\s)\\Q" + tableName + "\\E(?=\\s)";
return sql.replaceAll(regex, dynamicTableName);
}
return sql;
}
/**
* 生成动态表名,无改变返回 NULL
*
* @param metaObject 元对象
* @param sql 当前执行 SQL
* @param tableName 表名
* @return String
*/
String dynamicTableName(MetaObject metaObject, String sql, String tableName);
进一步优化方案
这里的方案,哪些表需要重写表名,需要硬编码到代码中,自定义表名列表。如何做成自动的呢?
1. 将需要使用不同表的mapper接口,分目录存放。
2. 自定义的tableNameHandlerMap,保存为静态对象,随时可以修改。
3. 在mybatis的Interceptor中,根据当前sql的目录路径和表名,动态设置tableNameHandlerMap。
目前项目只用到了几张表,优化方案,暂未提供代码例子。有更好方案,欢迎评论区交流,谢谢。
版权声明:本文为zjy_love_java原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。