基于MybatisPlus拦截器实现数据库关键字处理及官方做法

这篇具有很好参考价值的文章主要介绍了基于MybatisPlus拦截器实现数据库关键字处理及官方做法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 背景

有些老的数据库当中可能会有些字段和数据库关键字冲突,使用mybatisPlus执行Sql的时候有时候会执行失败,前段时间和群友讨论的时候他说遇到了这个问题,当时我提议让他用我以前写的一个自定义注解+mybatis拦截器实现权限控制里边的工具类改造一下。

他说不能实现,然后找到了官方的配置信息,也就是添加最下边的那个column-format, 具体配置如下:

mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml
  configuration:
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 返回类型为Map,显示null对应的字段
    call-setters-on-nulls: true
  global-config:
    # 关闭MP3.0自带的banner
    banner: true
    db-config:
      id-type: none
      # 默认数据库表下划线命名
      table-underline: true
      # 拦截器功能 处理数据库关键字
      column-format: "`%s`"

2. 自己实现一个关键字替换的方法

仅仅是一个初步的实现,具体的逻辑还需要变更一下。

主要的实现逻辑就是在字典表中配置上数据库的关键字,然后在Sql执行之前给BoundSql中的关键字替换一下。也就是keywordHandler方法的逻辑文章来源地址https://www.toymoban.com/news/detail-857233.html

package net.lesscoding.keywordhandler.interceptor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import net.lesscoding.keywordhandler.entity.SysDict;
import net.lesscoding.keywordhandler.mapper.SysDictMapper;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;


@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}),
        }
)
@Slf4j
@Component
@Order(20)
public class KeywordHandlerInterceptor implements Interceptor {

    @Autowired
    @Lazy
    private SysDictMapper dictMapper;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[0];
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        //获取参数,语句成立,表示sql语句有参数,参数格式是map形式
        Object parameter = args.length > 1 ? args[1] : null;

        //id为执行的mapper方法的全路径名,如com.xxx.xxxMapper.insertXxx
        String sqlId = mappedStatement.getId();
        Executor executor = (Executor) invocation.getTarget();
        //[BoundSql]封装mybatis最终产生的sql类
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        //获取节点的配置
        Configuration configuration = mappedStatement.getConfiguration();
        String sql = getSql(configuration, boundSql, sqlId, false);
        // 非select语句直接过滤
        if (!SqlCommandType.SELECT.equals(sqlCommandType) || sqlId.contains("SysDictMapper")) {
            return proceed(invocation, sqlId, sql);
        }
        String beforeSql = boundSql.getSql().replaceAll("[\\s]+", " ");
        log.info("之前的sql {}", beforeSql);
        //String afterSql = contactConditions(beforeSql, permissionControl, (Map)parameter);
        String afterSql = keywordHandler(beforeSql);
        log.info("关键字处理之后的sql {}", afterSql);
        ReflectUtil.setFieldValue(boundSql, "sql", afterSql);
        log.info("最终执行的sql {}", getSql(configuration, boundSql, sqlId, false));
        MappedStatement ms = (MappedStatement) args[0];
        Object parameterObject = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        ResultHandler resultHandler = (ResultHandler) args[3];
        //可以对参数做各种处理
        CacheKey cacheKey = executor.createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return executor.query(ms, parameterObject, rowBounds, resultHandler, cacheKey, boundSql);
    }

    private String keywordHandler(String beforeSql) {
        List<SysDict> list = dictMapper.selectList(new QueryWrapper<SysDict>()
                .eq("dict_type", "keyword"));
        for (SysDict item : list) {
            beforeSql = beforeSql.replaceAll(StrUtil.format("(?<!_){}", item.getDictCode()), item.getDictValue());
        }
        return beforeSql;
    }

    public static void main(String[] args) {
        String str = "replace name,but not replace _name, test_name,login_name, user_name";
        System.out.println(str.replaceAll("(?<!_)name", "`name`"));
    }

    private Object proceed(Invocation invocation, String sqlId, String sql) throws Throwable{
        try {
            //执行完上面的任务后,执行原有sql的执行过程
            return invocation.proceed();
        } catch (InvocationTargetException e) {
            log.error("当前sql执行错误\n {} \n, 方法id ::: {}\n 执行sql ::: {}", e.getMessage(), sqlId, sql);
            log.error("sql执行异常", e);
            throw new RuntimeException("sql执行异常" + sqlId);
        }
    }



    /**
     * 获取最终的sql语句
     *
     * @param configuration 节点配置
     * @param boundSql      BoundSql
     * @param sqlId         mapper方法的全路径名
     * @param flag          是否需要拼接mapper方法的全路径名
     * @return String
     */
    private String getSql(Configuration configuration, BoundSql boundSql, String sqlId, boolean flag) throws NoSuchFieldException {
        String sql = showSql(configuration, boundSql);
        return flag ? sqlId + ":" + sql : sql;
    }


    /**
     * 进行?的替换
     *
     * @param configuration 节点的配置
     * @param boundSql      BoundSql
     * @return String
     */
    private String showSql(Configuration configuration, BoundSql boundSql) {
        //获取参数
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        //sql语句
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (CollUtil.isNotEmpty(parameterMappings) && parameterObject != null) {
            //获取类型处理器
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            //对应的类型则进行替换
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object objectValue = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(objectValue)));
                    }
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        //动态sql
                        Object additionalParameter = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(additionalParameter)));
                    }
                    //else {
                    //    sql = sql.replaceFirst("\\?", "");
                    //}
                }
            }
        }
        return sql;
    }

    /**
     * 参数二次处理
     *
     * @param parameterObject parameterObject
     * @return String
     */
    private String getParameterValue(Object parameterObject) {
        String format = "'%s'";
        String value = "";
        if (null != parameterObject) {
            value = String.valueOf(parameterObject);
        }
        if (parameterObject instanceof String) {
            value = String.valueOf(parameterObject);
        }
        if (parameterObject instanceof Date) {
            //DateFormat dateTimeInstance = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT,
            // Locale.CHINA);
            //value = dateTimeInstance.format(parameterObject);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            value = sdf.format((Date) parameterObject);
        }
        if (parameterObject instanceof LocalDateTime) {
            value = ((LocalDateTime) parameterObject).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }
        if (parameterObject instanceof LocalDate) {
            value = ((LocalDate) parameterObject).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }
        if (parameterObject instanceof LocalTime) {
            value = ((LocalTime) parameterObject).format(DateTimeFormatter.ofPattern("HH:mm:ss"));
        }
        if (parameterObject instanceof Timestamp) {
            value = ((Timestamp) parameterObject).toString();
        }
        return String.format(format, value);
    }

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

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}

到了这里,关于基于MybatisPlus拦截器实现数据库关键字处理及官方做法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MyBatis Plus 拦截器实现数据权限控制

    上篇文章介绍的MyBatis Plus 插件实际上就是用拦截器实现的,MyBatis Plus拦截器对MyBatis的拦截器进行了包装处理,操作起来更加方便 2.1、InnerInterceptor MyBatis Plus提供的InnerInterceptor接口提供了如下方法,主要包括:在查询之前执行,在更新之前执行,在SQL准备之前执行 2.2、编写简

    2024年01月17日
    浏览(33)
  • 数据权限拦截器,多租户拦截器

    WEB类型软件产品,在Java(SpringBoot)+MybatisPlus架构场景下,本文针对下面两个问题,提供解决方案: 多租户的产品,想在表内级别上,实现租户数据隔离(分表、分库方案不在本文讨论范围内)。 ToB、ToG类型的软件产品,需要实现数据权限鉴权。例如用户数据、部门数据、租户

    2024年02月02日
    浏览(33)
  • 自定义注解与拦截器实现不规范sql拦截(拦截器实现篇)

    最近考虑myBatis中sql语句使用规范的问题,如果漏下条件或者写一些不规范语句会对程序性能造成很大影响。最好的方法就是利用代码进行限制,通过拦截器进行sql格式的判断在自测环节就能找到问题。写了个简单情景下的demo,并通过idea插件来将myBatis的mapper方法都打上拦截器

    2024年01月22日
    浏览(36)
  • SpringBoot加入拦截器——登录拦截器的实现

            拦截器 Interceptor 在 Spring MVC 中的地位等同于 Servlet 规范中的过滤器 Filter,拦截的是处理器的执行,由于是全局行为,因此常用于做一些通用的功能,如请求日志打印、权限控制等。         核心原理:AOP思想 preHandle:  预先处理,在目标的controller方法执行之前,进行

    2024年02月15日
    浏览(32)
  • Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回)

    目录 1. 用户登录权限校验 1.1 最初用户登录权限效验 1.2 Spring AOP 用户统⼀登录验证 1.3 Spring 拦截器 (1)创建自定义拦截器 (2)将自定义拦截器添加到系统配置中,并设置拦截的规则 1.4 练习:登录拦截器 (1)实现 UserController 实体类 (2)返回的登录页面:login.html (3)实

    2024年02月12日
    浏览(34)
  • springboot实现拦截器

    内容:继承 HandlerInterceptorAdapter 并实现 WebMvcConfigurer , 拦截器中的方法将preHandle-Controller-postHandle-affterCompletion的顺序执行。 注意:只有preHandle方法返回true时后面的方法才会执行。当拦截器链存在多个拦截器时,postHandle在所有拦截器内的所有拦截器返回成功时才会调用,而

    2024年02月02日
    浏览(38)
  • 自定义拦截器实现

    在 Spring MVC 框架中, 拦截器作为一种机制, 用于对请求进行拦截. 拦截器可以在请求进入处理器之前、处理器返回处理之后、视图渲染之前等各个环节进行拦截. 拦截器通常用于实现一下功能 : 鉴权和身份认证 日志记录和统计 请求参数和校验和过滤 缓存和性能优化 路径重定向

    2024年02月09日
    浏览(40)
  • Spring拦截器实现鉴权

    拦截器(Interceptor)类似于Servlet中的过滤器,主要用于拦截用户请求并做出相应的处理,例如拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。拦截器允许自定义预处理(Pre-Processing),在其中可以选择禁止对应Handler 的执行;也允许自定义后处理(Post-Pre

    2024年02月11日
    浏览(28)
  • MybatisPlusInterceptor实现sql拦截器(超详细)

    1 . 导入pom 2 . 配置下MybatisPlus的yml 3 . 实体类 4 .  DTO 5 . MybatisPlus的config 6 . controller 7 . 测试  成功实现sql拦截并进行拼接

    2024年02月11日
    浏览(43)
  • 自定义注解与拦截器实现不规范sql拦截(自定义注解填充插件篇)

    在自定义注解与拦截器实现不规范sql拦截(拦截器实现篇)中提到过,写了一个idea插件来辅助对Mapper接口中的方法添加自定义注解,这边记录一下插件的实现。 在上一篇中,定义了一个自定义注解对需要经过where判断的Mapper sql方法进行修饰。那么,现在想使用一个idea插件来

    2024年01月23日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包