MyBatis Plus应用(五)schema的优雅实现

MyBatisPlus
placeholder image
admin 发布于:2023-06-19 09:45:08
阅读:loading

1.背景介绍

在我个人的企业级开发实践中,使用数据库的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插件的启发来分析一下,然而事随我愿,从全局非常优雅的解决了,只是不知道这种实现是不是比较专业。

2.实现方式一(不推荐)

使用插件的形式来整合,参考动态表的实现,按表名关键字来适配,动态替换,假设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的实现,还是老老实实的用作于动态表的实现吧;

3.实现方式二(推荐)

需要结合上述方式一的代码,仅仅关注调用该方法的逻辑即可。

在后续的源码分析中还是把目光放到了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>


 点赞


 发表评论

当前回复:作者

 评论列表


留言区