代码设计篇—数据权限拼装sql通用解决方案

笔者最近仿照PageHelper写了一个拼装sql的组件,需要灵活拼装多个字段,在遇到多子查询时难以做到通用。笔者最终放弃对sql进行截取拼装,想了一个比较讨巧的方案,仿照Mybatis自定义一个特殊字符,然后在拦截器里替换。不过这种方案也比较坑,需要在sql里列出要过滤的字段,等笔者想出更好的方案时再进行补充,下面先看下用特殊字符的方案。

@Component
@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)
public class DataPermissionInterceptor implements Interceptor {

        @Override
        public Object intercept(Invocation invocation) throws Throwable {
                Object[] args = invocation.getArgs();
                MappedStatement ms = (MappedStatement) args[0];
                Object parameter = args[1];
                RowBounds rowBounds = (RowBounds) args[2];
                ResultHandler resultHandler = (ResultHandler) args[3];
                Executor executor = (Executor) invocation.getTarget();
                BoundSql boundSql;
                CacheKey cacheKey;
                if(args.length == 4){
                        //4 个参数时
                        boundSql = ms.getBoundSql(parameter);
                        cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
                } else {
                        //6 个参数时
                        cacheKey = (CacheKey) args[4];
                        boundSql = (BoundSql) args[5];
                }

                Configuration configuration = ms.getConfiguration();
                String sql = this.buildSql(boundSql.getSql());
                BoundSql dataFilterBoundSql = new BoundSql(configuration, sql, boundSql.getParameterMappings(), parameter);
                return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, dataFilterBoundSql);
        }

        private String buildSql(String sql) {
                UserInfo userInfo = UserInfoThreadLocal.get();
                //将登陆的用户信息需要过滤的字段放入Map里
                Map<String, Object> fieldMap = this.fieldMap(userInfo);
                for (Map.Entry<String, Object> entry : fieldMap.entrySet()) {
                        //替换过滤字段,例#userId
                        sql = sql.replace("#" + entry.getKey(), entry.getValue().toString());
                }
                return sql;
        }

        private Map<String, Object> fieldMap(UserInfo userInfo) {
                Class<? extends UserInfo> infoClass = userInfo.getClass();
                Map<String, Object> fieldMap = new HashMap<>();
                for (Field field : infoClass.getDeclaredFields()) {
                        try {
                                field.setAccessible(true);
                                fieldMap.put(field.getName(), field.get(userInfo));
                                field.setAccessible(false);
                        } catch (IllegalAccessException e) {
                                e.printStackTrace();
                        }
                }
                return fieldMap;
        }

        @Override
        public Object plugin(Object target) {
                return Plugin.wrap(target, this);
        }

        @Override
        public void setProperties(Properties properties) {}

上面是拦截器里的代码,Interceptor是Mybatis提供的。笔者是把登陆的userInfo信息里需要自动拼装的字段放入了一个Map里,然后对Map遍历替换掉sql里相应的字段。sql代码段如下:

    <select id="queryUserInfo">
        select id, name
        from User u
        where u.id = '#userId'
    </select>

sql里的字段是’#userId’,加单引号是防止报错,#+字段名是用来在拦截器里进行替换。

猜你喜欢

转载自blog.csdn.net/weixin_45497155/article/details/105980116
今日推荐