• 通过mybatis-plus的自定义拦截器实现控制 mybatis-plus的全局逻辑删除字段的控制 (修改其最终执行的sql中的where条件)


    需求:过滤部分请求不实现mybatis-plus的逻辑删除

    看到网上关于mybatis-plus的自定义拦截器的文章有的少 想了想自己写了一篇 欢迎参考 指正

    通过springboot的拦截器 在请求进来时 标记需要实现的需求的逻辑

    import lombok.Data;
    
    @Data
    public class SyncBo {
        private Boolean needHandler;
    }
    

    上数据放在threadlocal以上

    public final class SyncContextHolder {
        private static final ThreadLocal CONTEXT = ThreadLocal.withInitial(SyncBo::new);
    
        private SyncContextHolder() {
        }
    
        /**
         * 获取配置
         **/
        public static SyncBo getContext() {
            return CONTEXT.get();
        }
    
        public static void setContext(SyncBo bo) {
            CONTEXT.set(bo);
        }
    
        /**
         * 清空数据
         **/
        public static void clean() {
            CONTEXT.remove();
        }
    }
    

    实现springboot的拦截器

    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.collections4.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.jetbrains.annotations.NotNull;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.List;
    import java.util.Objects;
    import java.util.stream.Collectors;
    
    @Component
    @Slf4j
    @RequiredArgsConstructor
    public class DataAuthInterceptor implements HandlerInterceptor {
    
        private final CommonService commonService;
    
        private final ChsPubService chsPubService;
    
        @Override
        public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
            SyncBo syncBo = SyncContextHolder.getContext();
            // 项目中的字典类。 你可以自己处理该部分的数据。反正最终要得到一个List用于判断uri是否在你需要处理的请求中
            List syncUriList = commonService.getDictDataInfoByDictId(null, "SyncUri", null);
            if (CollectionUtils.isNotEmpty(syncUriList)) {
                List syncUris = syncUriList.stream().map(DictData::getValue).collect(Collectors.toList());
                syncBo.setNeedHandler(syncUris.contains(request.getRequestURI()));
            } else {
                syncBo.setNeedHandler(false);
            }
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
      
    		//以下两个方法 默认实现即可
        @Override
        public void postHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, ModelAndView modelAndView) throws Exception {
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        @Override
        public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) throws Exception {
            DataAuthContextHolder.clean();
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    }
    

    将实现的拦截器注册上spring中 并设定拦截所有的请求

    import lombok.RequiredArgsConstructor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    @RequiredArgsConstructor
    public class AuthWebMvcConfig implements WebMvcConfigurer {
    
        private final DataAuthInterceptor dataAuthInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
          	//设定拦截所有的请求
            registry.addInterceptor(dataAuthInterceptor).addPathPatterns("/**");
        }
    }
    

    到了mybatis-plus部分

    先实现一个自己的拦截器 其中发现了一个jsqlparser解析的报错。该报错在我的另外一篇博客有解决方案

    https://www.cnblogs.com/dkpp/p/17812677.html

    以下代码我也不太想解释了 涉及到mybatis-plus的源码和jsqlparser的源码。

    反正就是要实现一个处理器 你所需要的改造方法在你的处理器中

    import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
    import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
    import com.baomidou.mybatisplus.core.toolkit.PluginUtils.MPBoundSql;
    import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
    import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
    import lombok.*;
    import net.sf.jsqlparser.expression.Expression;
    import net.sf.jsqlparser.statement.select.PlainSelect;
    import net.sf.jsqlparser.statement.select.Select;
    import net.sf.jsqlparser.statement.select.SelectBody;
    import net.sf.jsqlparser.statement.select.SetOperationList;
    import net.sf.jsqlparser.statement.update.Update;
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.mapping.SqlCommandType;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.List;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString(callSuper = true)
    @EqualsAndHashCode(callSuper = true)
    public class SyncInterceptor extends JsqlParserSupport implements InnerInterceptor {
        private SyncHandler syncHandler;
    
        @Override
        public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
            if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) return;
            MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
            mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));
        }
    
        @Override
        public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
            PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
            MappedStatement ms = mpSh.mappedStatement();
            SqlCommandType sct = ms.getSqlCommandType();
            if (sct == SqlCommandType.UPDATE) {
                if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {
                    return;
                }
                BoundSql boundSql = mpSh.boundSql();
                MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
                String sql = boundSql.getSql();
                // jsqlparser目前不支持\n\n\n的解析 需要手动处理
                sql = sql.replaceAll("\n","");
                mpBs.sql(parserMulti(sql, ms.getId()));
            }
        }
    
        @Override
        protected void processUpdate(Update update, int index, String sql, Object obj) {
            update.setWhere(this.handlerWhere(update.getWhere(), (String) obj));
        }
    
        @Override
        protected void processSelect(Select select, int index, String sql, Object obj) {
            SelectBody selectBody = select.getSelectBody();
            if (selectBody instanceof PlainSelect) {
                ((PlainSelect) selectBody).setWhere(this.handlerWhere(((PlainSelect) selectBody).getWhere(), (String) obj));
            } else if (selectBody instanceof SetOperationList) {
                SetOperationList setOperationList = (SetOperationList) selectBody;
                List selectBodyList = setOperationList.getSelects();
                selectBodyList.forEach(s -> ((PlainSelect) s).setWhere(this.handlerWhere(((PlainSelect) s).getWhere(), (String) obj)));
            }
        }
    
        protected Expression handlerWhere(Expression where, String whereSegment) {
            return syncHandler.getSqlSegment(where, whereSegment);
        }
    }
    
    

    处理器 其中excludedPaths参数是在构建的时候传进来的

    并且目前我需要处理的sql的mappedStatementId 为 updateById 和 selectById 两个

    这两个方法下是一定只有两个条件的where条件的(至少项目目前是这么一个情况 所以我只判断了AndExpression类型)

    import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
    import com.chinacreator.c2.chs.bo.pub.SyncBo;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import net.sf.jsqlparser.expression.Expression;
    import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
    import org.apache.commons.lang3.BooleanUtils;
    
    import java.util.Arrays;
    import java.util.Objects;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    @Slf4j
    @RequiredArgsConstructor
    public class SyncHandler implements DataPermissionHandler {
    
        private final String[] excludedPaths;
    
        @Override
        public Expression getSqlSegment(Expression where, String mappedStatementId) {
            SyncBo syncBo = SyncContextHolder.getContext();
            if (BooleanUtils.isTrue(syncBo.getNeedHandler())) {
                AtomicBoolean needRemoveAtomic = new AtomicBoolean(false);
                Arrays.stream(excludedPaths).forEach(excludedPath -> {
                    if (mappedStatementId.contains(excludedPath)) {
                        needRemoveAtomic.set(true);
                    }
                });
                if (needRemoveAtomic.get())
                    // 目前可以只处理AndExpression的情况
                    // 原因为调用的方法是 updateById 和 selectById 方法
                    // 这两个方法默认只有一个where条件加一个vali_flag = '1'
                    // 也就是两个where条件所以可以只判断AndExpression
                    if (where instanceof AndExpression) {
                        AndExpression andExpression = (AndExpression) where;
                        String leftString = Objects.nonNull(andExpression.getLeftExpression().toString()) ? andExpression.getLeftExpression().toString() : "";
                        String rightString = Objects.nonNull(andExpression.getRightExpression().toString()) ? andExpression.getRightExpression().toString() : "";
                        if (leftString.contains("vali_flag = '1'"))
                            where = andExpression.getRightExpression();
                        if (rightString.contains("vali_flag = '1'"))
                            where = andExpression.getLeftExpression();
                    }
            }
            return where;
        }
    }
    

    然后是将拦截器注册到mybaits-plus的指定位置

    我还实现了数据权限的拦截器 这里就不展开了

    import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
    import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
    import com.baomidou.mybatisplus.core.toolkit.IdWorker;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.Map;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.LinkedBlockingQueue;
    
    @Slf4j
    @Configuration
    public class MyBatisPlusConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(@Value("#{'${logic-deleted.excluded.path}'.empty ? null : '${logic-deleted.excluded.path}'.split(';')}") String[] excludedPaths) {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 我还实现了数据权限的拦截器 这里就不展开了
            // interceptor.addInnerInterceptor(new DataPermissionInterceptor(new GlobalDataAuthHandler()));
            interceptor.addInnerInterceptor(new SyncInterceptor(new SyncHandler(excludedPaths)));
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            return interceptor;
        }
    }
    
    

    最后是配置文件

    # mybatis-plus关于逻辑删除字段的配置
    mybatis-plus.global-config.db-config.logic-delete-field=vali_flag
    mybatis-plus.global-config.db-config.logic-not-delete-value=1
    
    # 需要移除的mappedStatementId
    logic-deleted.excluded.path=selectById;updateById
    

    最后是结果

    SyncInterceptor - original SQL: SELECT xxx FROM xxx WHERE xxx = ? AND vali_flag = '1'
    SyncInterceptor - SQL to parse, SQL: SELECT xxx FROM xxx WHERE xxx = ? AND vali_flag = '1'
    SyncInterceptor - parse the finished SQL: SELECT xxx FROM xxx WHERE pgid = ?
    xxxMapper.selectById - ==>  Preparing: SELECT xxx FROM xxx WHERE pgid = ?
    SyncInterceptor - original SQL: UPDATE xxx  SET xxx  WHERE xxx=?  AND vali_flag='1'
    SyncInterceptor - SQL to parse, SQL: UPDATE xxx  SET xxx  WHERE xxx=?  AND vali_flag='1'
    SyncInterceptor - parse the finished SQL: UPDATE xxx SET xxx WHERE xxx = ?
    Mapper.updateById - ==>  Preparing: UPDATE xxx SET xxx WHERE xxx = ?
    

    可以看到已经处理掉了

  • 相关阅读:
    8月8日下午6:00面试总结
    htop安装使用
    攻防世界-MISC:a_good_idea
    多个Map进行内容合并
    清洁机器人--音频方案之基于国民MCU IO控制以及SPI2的唯创WT588 语音升级方案
    什么是0day漏洞?如何预防0day攻击?
    使用函数计算自定义运行时快速部署一个 SpringBoot 项目 | 文末有礼
    收集路径下的html并写到根目录index.html
    windows搭建elasticsearch和elasticsearch-head/kibana
    Spread for WPF 16.2 Crack -Winform
  • 原文地址:https://www.cnblogs.com/dkpp/p/17812868.html