MyBatis Plus应用(五)schema的优雅实现
MyBatisPlusadmin 发布于:2023-06-19 09:45:08
阅读:loading
在我个人的企业级开发实践中,使用数据库的schema是经常会遇到的,特别在于一些连接其它系统的数据库时,时常被授以小账号的只读用户,所以在使用过程中所有相关用户的对象均需要使用shcema的方式引用,在JPA中内置了一些指令表达式可以用于动态的替换schema,比如在Oracle中使用{h-schema},在MySQL中使用{h-catalog},而在MyBatisPlus中貌似只给了全局的参数配置“mybatis-plus.global-config.db-config.schema”来实现基于MyBatisPlus API的schema,但这种方式在Mapper的xml中不生效,或者时我并不知道这种方式在XML中如何去使用。
本站2.0的实现专门使用到了MyBatisPlus(学习并使用了),很巧妙的避开了schema这块的问题,然而同事近期的项目中使用了MyBatisPlus和PG库,问了问我,本着严谨虚心的态度给科普了一翻,经过一番尝试后,并未找到合理和优雅的动态schema的实现,后来受前一篇文章动态表的启发,想着借助MyBatisPlus插件的启发来分析一下,然而事随我愿,从全局非常优雅的解决了,只是不知道这种实现是不是比较专业。
使用插件的形式来整合,参考动态表的实现,按表名关键字来适配,动态替换,假设schema名称为abc,参考代码如下:
@Bean
public PaginationInterceptor getPaginationInterceptor(){
PaginationInterceptor interceptor = new PaginationInterceptor();
//构建动态标名解析器
DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
Map<String, ITableNameHandler> tableNameMap = Maps.newHashMap();
tableNameMap.put("Sys_operationlog", (metaObject, sql, tableName) -> {
RoutingStatementHandler handler = (RoutingStatementHandler) metaObject.getOriginalObject();
BoundSql boundSql = handler.getBoundSql();
Object parameterObject = boundSql.getParameterObject();
if(parameterObject instanceof SysOperationLog) {
SysOperationLog sysOperationLog = (SysOperationLog) parameterObject;
//日志表每个月初始化一次
return sysOperationLog.getTableNameCondition();
}
MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) handler.getBoundSql().getParameterObject();
Set<Map.Entry<String , Object>> set = paramMap.entrySet();
for (Map.Entry<String, Object> entry : set) {
Object value = entry.getValue();
if(value instanceof DynamicTableName) {
DynamicTableName dynamicTableName = (DynamicTableName) value;
return dynamicTableName.getTableNameCondition();
}
}
return tableName;
});
tableNameMap.put("@sys_account" , (metaObject, sql, tableName) -> "abc.sys_account");
tableNameMap.put("schema_sys_user" , (metaObject, sql, tableName) -> "abc.sys_user");
dynamicTableNameParser.setTableNameHandlerMap(tableNameMap);
interceptor.setSqlParserList(Lists.newArrayList(dynamicTableNameParser , this.getAbc()));
return interceptor;
}
(1)上述实现主要是使用 tableNameMap.put 表名称的形式来实现的动态表,在Mapper的xml中直接写成 @sys_account或schema_sys_user的形式,在执行时,将被输出abc.sys_account;
(2)又验证了一下这块的put不能传递正则表达式,如果可以传递正则那还是比较给力的;
(3)这种方式真的是不适合用于解决动态schema的实现,还是老老实实的用作于动态表的实现吧;
需要结合上述方式一的代码,仅仅关注调用该方法的逻辑即可。
在后续的源码分析中还是把目光放到了interceptor.setSqlParserList这块的插件解析SQL上,经过分析最终增加如下代码后,非常优雅的解决了schema的问题,参考代码如下:
private ISqlParser getAbc() {
return (metaObject, sql) -> {
if (StringUtils.containsIgnoreCase(sql , "{h-schema}")) {
return SqlInfo.newInstance().setSql(sql.replace("{h-schema}" , "abc."));
}
return SqlInfo.newInstance().setSql(sql);
};
}
(1)相当于在SQL语句中读取到{h-schema}的特色字符串后,将其替换为从后台获取到的实际schema;
(2)为了兼容JAP的动态schema写法,本例约定使用{h-schema}的特色字符来替换schema名称;
(3)参考Mapper的xml写法,表名称前面都增加了{h-schema},输出的SQL正是预期,参考如下:
<!-- 用户信息列表查询 -->
<select id="querySysUserPage" resultType="cn.chendd.blog.admin.system.sysuser.vo.SysUserManageResult"
parameterType="map">
SELECT
a.accountId,
a.username,
a.status,
b.userId,
b.realName,
b.email,
b.createTime,
b.lastLoginTime,
b.userSource
FROM {h-schema}sys_account a
LEFT JOIN {h-schema}sys_user b ON a.accountId = b.accountId
WHERE
a.dataStatus = 'USABLE'
order by b.userSource asc , b.createTime asc
</select>
点赞