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

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

时间:2023-11-06 15:58:10浏览次数:34  
标签:自定义 xxx plus sql org mybatis import com public

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

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

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

import lombok.Data;

@Data
public class SyncBo {
    private Boolean needHandler;
}

上数据放在threadlocal以上

public final class SyncContextHolder {
    private static final ThreadLocal<SyncBo> 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<String>用于判断uri是否在你需要处理的请求中
        List<DictData> syncUriList = commonService.getDictDataInfoByDictId(null, "SyncUri", null);
        if (CollectionUtils.isNotEmpty(syncUriList)) {
            List<String> 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<SelectBody> 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 = ?

可以看到已经处理掉了

标签:自定义,xxx,plus,sql,org,mybatis,import,com,public
From: https://www.cnblogs.com/dkpp/p/17812868.html

相关文章

  • 带你理解 Java 8 的函数式接口使用和自定义
    函数式接口是Java8引入的一种接口,用于支持函数式编程。函数式接口通常包含一个抽象方法,可以被Lambda表达式或方法引用所实现。在本文中,我们将深入探讨函数式接口的概念、用途以及如何创建和使用函数式接口。什么是函数式接口函数式接口是只包含一个抽象方法的接口。但是默认方......
  • 自定义xunit测试用例的执行顺序
    有的时候我们会对程序进行单元测试,为了测试的效果以及后期的维护,我一般会将各个测试拆开,根据需要测试的类分到各个类型中,不过在实际操作的时候就出现了一些意想不到的问题,各个测试的执行是乱序的,按照我自己写测试的习惯,假如我需要测试新写的增删改查的功能,我会将......
  • SpringBoot通过自定义注解+反射机制比较两个对象不同的属性值
    publicclassFieldComparisonUtil{/**•直接返回一个新的对象,并且对象的值只有被修改的部分••@paramold•@paramsource•@paramisParent•@paramtarget目标对象•@return/**•@paramold进行属性比较的原始数据•@paramsource进行属性比......
  • Java 获取自定义注解 字段值 及 注解值
    自定义注解packagecom.jianmu.bean.annotation;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/**[email protected]......
  • 038-第三代软件开发-简易视频播放器-自定义Slider (二)
    第三代软件开发-简易视频播放器-自定义Slider(二)文章目录第三代软件开发-简易视频播放器-自定义Slider(二)项目介绍简易视频播放器自定义Slider(二)横向纵向关键字:Qt、Qml、关键字3、关键字4、关键字5项目介绍欢迎来到我们的QML&C++项目!这个项目结合了QML(QtMeta-......
  • 034-第三代软件开发-自定义Slider(一)
    第三代软件开发-自定义Slider(一)文章目录第三代软件开发-自定义Slider(一)项目介绍自定义Slider(一)总结一下关键字:Qt、Qml、Slider、position、关键字5项目介绍欢迎来到我们的QML&C++项目!这个项目结合了QML(QtMeta-ObjectLanguage)和C++的强大功能,旨在开发出色的......
  • Spring自定义数据校验并实现国际化功能
    通常,当我们需要验证用户输入时,SpringMVC提供标准的预定义验证器。我们会引入spring-boot-starter-validation依赖来实现数据校验功能。但是,当我们需要验证特定类型的输入时,我们就需要创建自己的自定义校验逻辑。这里我们取一个相对简单的校验手机号码的功能来实现。为了校验手......
  • 苹果iOS 17.2年底推送:iPhone 15 Pro的自定义操作按钮功能升级
    据报道,苹果会在年底推送iOS17.2版本,新版系统将会修复iPhone15系列WiFi速度慢的问题。与此同时,iOS17.2将会带来翻译功能,iPhone15Pro的自定义操作按钮切换到翻译选项后,按住会弹出一个翻译窗口,用于翻译设备听到的语音内容。除此之外,这枚自定义操作按钮还可以设置为其它很多功......
  • 【mysql】获取某个表所有列名【mybatis】
    方法1:[仅指定表名]selectCOLUMN_NAMEfrominformation_schema.COLUMNSwheretable_name='your-table-name'; 方法2:[指定表名+数据库名]selectCOLUMN_NAMEfrominformation_schema.COLUMNSwheretable_name='your-table-name'andtable_schema='your-DB......
  • POWERSHELLPLUS调试脚本方法
    在之前的博文中,我曾经提到过,使用PowerShellPlus调试脚本的方法我没有找到,从而使用PowerGUI调试的。今天又研究了一下工具,发现还是可以调试的。也不知道是我那个版本的问题还是其它什么原因导致当时没有能够成功调试。这个工具的菜单和传统的方式还是有点不一样(这个应该是WIndows7风......