自定义注解与拦截器实现不规范sql拦截(拦截器实现篇)

这篇具有很好参考价值的文章主要介绍了自定义注解与拦截器实现不规范sql拦截(拦截器实现篇)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

需求简介

使用myBatis拦截器对Mapper的sql进行判断,对增加了自定义注解修饰的方法进行where条件的判断,存在where子句再执行,否则抛出异常。

具体实现

自定义注解

// 标志注解,用来表示mapper方法需要经过where条件存在与否的判断
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface WhereConditionCheck {
}

限定method修饰,同时指定runtime,以在运行过程中判断是否被该注解修饰。

拦截器实现类

WhereConditionInterceptor拦截器判断类

@Intercepts({
        // 拦截query方法
        @Signature(type = Executor.class, method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class})
})
@Component
@Slf4j
// 利用myBatis拦截器去获取对应的语句,where条件放行,无where阻止
public class WhereConditionInterceptor implements Interceptor {

首先利用@Intercepters注解,定义拦截器拦截的方法。这些方法来自Mybatis执行时调用的Executor接口的实现类,并通过args参数的设置来唯一指定同名的重载方法。

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取sql信息
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1]; // 参数
        BoundSql sql = mappedStatement.getBoundSql(parameter);

        // 获取原始调用方法的注解信息
        String id = mappedStatement.getId();
        String className = id.substring(0, id.lastIndexOf("."));
        String methodName = id.substring(id.lastIndexOf(".") + 1);
        Method[] methods = Class.forName(className).getMethods();
        // 不考虑方法重载
        for(Method method:methods){
            if(method.getName().equals(methodName) && method.isAnnotationPresent(WhereConditionCheck.class)){
                // 执行拦截
                boolean checkResult = checkIfWhereExist(sql);
                if(!checkResult){
                    // 根据sql类型判断抛出异常的语句
                    SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
                    if(sqlCommandType.equals(SqlCommandType.SELECT)) {
                        log.error("query error, sql='" + sql.getSql() + "''");
                        throw new RuntimeException("Query methods must contain where condition. method:" + id);
                    } else if(sqlCommandType.equals(SqlCommandType.DELETE)) {
                        log.error("delete error, sql='" + sql.getSql()  + "''");
                        throw new RuntimeException("Delete methods must contain where condition. method:" + id);
                    }
                }
            }
        }

        return invocation.proceed();
    }

重写intercept方法,利用invocation来获取之前定义的拦截方法的具体参数对象,利用第一个参数MappedStatement对象来获取具体的sql信息。需要注意的是,为了获取原Mapper接口中定义方法的注解信息,我们需要利用getId获取完整的接口名和类名,并利用反射获取对应的方法对象,以判断对应方法是否存在@WhereConditionCheck注解。

    private boolean checkIfWhereExist(BoundSql sql){
        String sqlStr = sql.getSql();
        sqlStr = sqlStr.toUpperCase();
        return sqlStr.contains("WHERE");
    }

具体的判断就直接将字符串转成大写后进行匹配即可。

简单使用

Mapper接口

@Mapper
public interface UserMapper {
    @Select("select * from user where code = #{code}")
    @WhereConditionCheck
    public User findUserByCode(String code);

    @Select("select * from user")
    // 全量查询,需要拦截
    @WhereConditionCheck
    public List<User> findUserListWhole();

简单设置两个方法,where子句和全量查询。

controller

@Controller
@RequestMapping("/user")
public class WhereConditionCheckController {

    @Autowired
    UserMapper userMapper;

    @ResponseBody
    @RequestMapping("/queryWhole")
    // 全量查询
    public String getWholeUser(){
        try {
            List<User> userList = userMapper.findUserListWhole();
            return JSONObject.toJSONString(userList);
        }catch(Exception ex){
            // 获取异常栈最底层以显示自定义信息
            Throwable cause = ex;
            Throwable result = null;
            while(cause != null){
                result = cause;
                cause = cause.getCause();
            }
            return result.getMessage();
        }
    }

    @ResponseBody
    @RequestMapping("/queryByCode")
    // where查询
    public String getUser(String code){
        try{
            User user = userMapper.findUserByCode(code);
            return JSONObject.toJSONString(user);
        }catch(Exception ex){
            /// 获取异常栈最底层以显示自定义信息
            Throwable cause = ex;
            Throwable result = null;
            while(cause != null){
                result = cause;
                cause = cause.getCause();
            }
            return result.getMessage();
        }
    }
}

对两个方法进行使用访问。需要注意的是,为了返回拦截器中抛出方法设定的message,需要捕获到异常栈中底层的exception进行输出。

效果展示

postman进行调用:
自定义注解与拦截器实现不规范sql拦截(拦截器实现篇),java,sql,后端,java
全量查询被拒绝。
自定义注解与拦截器实现不规范sql拦截(拦截器实现篇),java,sql,后端,java
这边有做一个脱敏处理,不用管。可以看出能正常查询出来。

总结

简单展示了使用Mybatis拦截器进行where子句判断的方式,用完整的类名和方法名去定位自定义注解,比较麻烦的其实是如何显示最原始的异常信息。开头说的使用插件自动添加自定义注解的实现,放在自定义注解与拦截器实现不规范sql拦截(自定义注解填充插件篇)中来讲。文章来源地址https://www.toymoban.com/news/detail-814630.html

到了这里,关于自定义注解与拦截器实现不规范sql拦截(拦截器实现篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 防重复提交:自定义注解 + 拦截器(HandlerInterceptor)

    防重复提交:自定义注解 + 拦截器(HandlerInterceptor) 一、思路: 1、首先自定义注解; 2、创建拦截器实现类(自定义类名称),拦截器(HandlerInterceptor); 3、创建类:配置拦截器路径(拦截URL规则); 二、代码示例: 1、首先自定义注解; 2、创建拦截器实现类(自定义类名

    2024年02月10日
    浏览(29)
  • Springboot 自定义 Mybatis拦截器,实现 动态查询条件SQL自动组装拼接(玩具)

    ps:最近在参与3100保卫战,战况很激烈,刚刚打完仗,来更新一下之前写了一半的博客。 该篇针对日常写查询的时候,那些动态条件sql 做个简单的封装,自动生成(抛砖引玉,搞个小玩具,不喜勿喷)。 来看看我们平时写那些查询,基本上都要写的一些动态sql:   一个字段

    2024年02月12日
    浏览(34)
  • SpringCloud微服务实战——搭建企业级开发框架:微服务安全加固—自定义Gateway拦截器实现防止SQL注入/XSS攻击

     SQL注入是常见的系统安全问题之一,用户通过特定方式向系统发送SQL脚本,可直接自定义操作系统数据库,如果系统没有对SQL注入进行拦截,那么用户甚至可以直接对数据库进行增删改查等操作。   XSS全称为Cross Site Script跨站点脚本攻击,和SQL注入类似,都是通过特定方

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

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

    2024年02月09日
    浏览(37)
  • SpringBoot Redis 注解 拦截器来实现接口幂等性校验

    幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次 比如:订单接口, 不能多次创建订单 支付接口, 重复支付同一笔订单只能扣一次钱 支付宝回调接口, 可能会多次回调, 必须处理重复回调 普通表单提交接口, 因为网络超时等原因多次点击提

    2024年01月19日
    浏览(41)
  • Spring Boot入门(23):记录接口日志再也不难!用AOP和自定义注解给Spring Boot加上日志拦截器!

            在上两期中,我们着重介绍了如何集成使用 Logback 与 log4j2 日志框架的使用,今天我们讲解的主题依旧跟日志有关,不过不是使用何种开源框架,而是自己动手造。         Spring的核心之一AOP;AOP翻译过来叫面向切面编程, 核心就是这个切面. 切面表示从业务逻辑中

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

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

    2024年02月11日
    浏览(42)
  • 利用Mybatis拦截器实现自定义的ID增长器

    原生的Mybatis框架是没有ID自增器,但例如国产的Mybatis Plus却是支持,不过,Mybatis Plus却是缺少了自定属性的填充;例如:我们需要自定义填充一些属性,updateDate、createDate等,这时Mybatis Plus自带的ID自增器就无法满足需求;这种时候我们就需要自定义的ID增加器,可以自定义

    2024年02月19日
    浏览(32)
  • Mybatis拦截器注解@Intercepts与@Signature注解属性说明

    可能有些新手使用mybatis拦截器的时候可能没太懂@Signature注解中type,method,args的用法 首先mybatis拦截器可以拦截如下4中类型 Executor sql的内部执行器 ParameterHandler 拦截参数的处理 StatementHandler 拦截sql的构建 ResultSetHandler 拦截结果的处理 type:就是指定拦截器类型(ParameterHandl

    2024年02月05日
    浏览(31)
  • 【SpringMVC】JSR 303与拦截器注解使用

       JSR 303,它是Java EE(现在称为Jakarta EE)规范中的一部分。JSR 303定义了一种用于验证Java对象的标准规范,也称为Bean验证。         Bean验证是一种用于验证对象属性的框架,它可以确保对象符合特定的规则和约束。这些规则可以包括字段的非空性、长度限制、格式验证等。

    2024年02月07日
    浏览(35)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包