通过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<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 = ?
可以看到已经处理掉了
通过mybatis-plus的自定义拦截器实现控制 mybatis-plus的全局逻辑删除字段的控制 (修改其最终执行的sql中的where条件)的更多相关文章
- 【Spring Boot】Spring Boot之自定义拦截器
一.拦截器的作用 将通用的代码抽取出来,达到复用的效果.比如可以用来做日志记录.登录判断.权限校验等等 二.如何实现自定义拦截器 1)创建自定义拦截器类并实现HandlerInterceptor类 / ...
- Mybatis自定义拦截器与插件开发
在Spring中我们经常会使用到拦截器,在登录验证.日志记录.性能监控等场景中,通过使用拦截器允许我们在不改动业务代码的情况下,执行拦截器的方法来增强现有的逻辑.在mybatis中,同样也有这样的业务 ...
- spring mvc <mvc:annotation-driven/> 自定义拦截器不走
<mvc:annotation-driven/> 这个便签会注册2个自定义拦截器,所以导致请求过来就会自己去走注册的这2个拦截器和定义的一堆bean 但是这个便签是必须得定义的 直接贴代码 ...
- 12.Struts2自定义拦截器
12.自定义拦截器 拦截器是Struts2的一个重要特性.因为Struts2的大多数核心功能都是通过拦截器实现的. 拦截器之所以称之为“拦截器”,是因为它可以拦截Action方法的执行, ...
- 【Java EE 学习 35 下】【struts2】【struts2文件上传】【struts2自定义拦截器】【struts2手动验证】
一.struts2文件上传 1.上传文件的时候要求必须使得表单的enctype属性设置为multipart/form-data,把它的method属性设置为post 2.上传单个文件的时候需要在Act ...
- SpringMVC——自定义拦截器、异常处理以及父子容器配置
自定义拦截器: 一.若想实现自定义拦截器,需要实现 org.springframework.web.servlet.HandlerInterceptor 接口. 二.HandlerIntercepto ...
- Struts2 自定义拦截器
自定义拦截器(权限管理),包含了对ajax和表单请求的拦截 package com.interceptor; import java.io.IOException; import java.io.Pr ...
- Struts2自定义拦截器
1. 需求 自定义拦截器实现,用户登录的访问控制. 2. 定义拦截器类 public class LoginInterceptor extends AbstractInterceptor { @Ove ...
- SpringMvc自定义拦截器
SpringMvc也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义拦截器必须实现HandlerInterceptor接口 -preHandle():这个方法在业务处理器 ...
- struts2基础——自定义拦截器
一.自定义拦截器 默认的拦截器能实现的功能是有限的,Struts2 支持自定义拦截器. 二.拦截器类 1.实现 Interceptor 接口 2.继承 AbstractInterceptor 抽象类, ...
随机推荐
- 2023年郑州轻工业大学校赛邀请赛zzh
第一次参加线下赛体验很好,面包和酸奶很好吃.ABL三题难度超出我们的能力范围,没能写出来,C题在读完题后,我们三个简单交流了一下,确定思路后我写的代码,一次AC,很顺利.D题简单的01背包,但我在写代 ...
- Linux内核笔记(三)内核编程语言和环境
学习概要: Linux内核使用的编程语言.目标文件格式.编译环境.内联汇编.语句表达式.寄存器变量.内联函数 c和汇编函数之间的相互调用机制Makefile文件的使用方法. as86汇编语言语法 汇编 ...
- MarkdownQuote:简化 Markdown 中的代码引用!
MarkdownQuote:简化 Markdown 中的代码引用! 这是 SourceCodeTrace 项目之一,通过在 IDE 中提供一种便捷的方式,快速复制包含代码来源 Markdown 代码块 ...
- Go中 net/http 使用
转载请注明出处: net/http是Go语言标准库中的一个包,提供了实现HTTP客户端和服务器的功能.它使得编写基于HTTP协议的Web应用程序变得简单和方便. net/http包的主要用途包括: 实 ...
- Builder 生成器模式简介与 C# 示例【创建型2】【设计模式来了_2】
〇.简介 1.什么是生成器模式? 一句话解释: 在构造一个复杂的对象(参数多且有可空类型)时,通过一个统一的构造链路,可选择的配置所需属性值,灵活实现可复用的构造过程. 生成器模式的重心,在于分离 ...
- 用 Tensorflow.js 做了一个动漫分类的功能(一)
前言: 浏览某乎网站时发现了一个分享各种图片的博主,于是我顺手就保存了一些.但是一张一张的保存实在太麻烦了,于是我就想要某虫的手段来处理.这样保存的确是很快,但是他不识图片内容,最近又看了 mobil ...
- axios快速上手(简单使用)
axios对ajax请求进行了封装,并且使用promise的链式调用使得网络请求的代码逻辑更为清晰,同时支持async和await的编写方式使代码看起来像同步,更加方便于理解和阅读.axios这个库的 ...
- 【腾讯云 Cloud Studio 实战训练营】提升开发效率与协作:探索腾讯云 Cloud Studio 的强大功能与优势
一.前言 前几天发生了一个故事,发生了这样一个情景:一位新加入的同事刚刚入职不久,领取了一台崭新的电脑.随后,他投身于一个新项目,但却遇到了一个困扰:由于这台电脑没有管理员权限,他无法在上面安装所需的 ...
- Java - ReentrantLock锁分析
Java - JUC核心类AbstractQueuedSynchronizer(AQS)底层实现 一. AQS内部结构介绍 JUC是Java中一个包 java.util.concurrent . ...
- Pandas 使用教程 CSV
CSV(Comma-Separated Values,逗号分隔值,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本). CSV 是一种通用的.相对简单的文 ...