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

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 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> set = paramMap.entrySet();
            for (Map.Entry 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正是预期,参考如下:


4.个人博客

站内关于MybatisPlus的5篇文章,是以当时的最新版本3.3.1为例的,请注意版本,文章列表如下:

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

MyBatis Plus应用(四)动态表的实践

MyBatis Plus应用(三)分页插件的实践

Mybatis Plus应用(二)逻辑删除的实践

Mybatis Plus应用(一)简单介绍与环境搭建

你可能感兴趣的:(mybatisplus,schema,陈冬冬,Java个人博客,www.chendd.cn)