Mybatis Plugin拦截器处理特殊字符串

        书接上回,在mybatis查询之前可手动调用公共方法,将特殊字符进行转义。commons-beanutils和反射解决查询%和_数据问题

      但是,这么做还是不够方便,因为每次我都要在查询前调用方法,如果我忘记调用了,那测试大神又得给版本不通过了,这不就完蛋了个锤子了吗!

        之前想的是用切面去做,但是发现切面方式不好,原因在于在切入获取参数时,存在不确定性,并且较难获取到其入参的实体类。为了满足测试大神的要求,于是尝试使用mybatis拦截器,拦截select方法的入参,对入参进行转义,并且对存在入参为实体类时,通过注解+反射的方式获取需要转义的字符。其中@Param传参的话是不需要定义注解的,注解的目的是在入参为实体类时进行转义。

        定义注解+反射工具类

定义注解@SpecChar


/**
 * 特殊字符处理注解,主要用来解决测试大神提的%和_查询问题
 * 希望我司测试大神能满意
 * 希望我司测试大神增强自己的实力,不要只会体验
 * 真正的测试比开发要牛
 * 共勉!
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SpecChar {
}

引入commons-beanutils包,建议引入1.9.3

        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>

定义工具类setSpecChar(),将要转义的实体类传入此方法!

    /**
     * 处理特殊字符‘%’、‘_’
     * @param object
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     */
 public static void setSpecChar(Object object) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
 if (object == null) {
            return ;
        }

        for (Field field : getAllFields(object)) {
            if (field.getAnnotation(SpecChar.class) != null) {
                String name = field.getName();
                Object value = PropertyUtils.getSimpleProperty(object, name);
                if (value != null) {
                  String valueStr = (String) value;
                    if (valueStr.contains("%")) {
                        valueStr = valueStr.replaceAll("\\%","\\\\%");
                    }
                    if (valueStr.contains("_")) {
                  valueStr = valueStr.replaceAll("\\_","\\\\_");
                    }
                    PropertyUtils.setProperty(object, name, valueStr);
                }
            }
        }
    }
 /**
     * 获取类的所有属性,包括父类
     *
     * @param object
     * @return
     */
    public static Field[] getAllFields(Object object) {
        Class<?> clazz = object.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
}
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }

定义dealSpecChar,将要转义的字符串传入此方法!

    public static String dealSpecChar(String valueStr) {
        if (valueStr.contains("%")) {
            valueStr = valueStr.replaceAll("\\%","\\\\%");
        }
        if (valueStr.contains("_")) {
            valueStr = valueStr.replaceAll("\\_","\\\\_");
        }
        return valueStr;
    }

定义MyBatis拦截器

 定义拦截器,在查询方法类型为Select时,将带有特殊字符的参数,进行转义。本案例中,也引入了update和insert方法,用于增强更新和创建时间,亦可进行参考。

/**
 * mybatis拦截获取自动创建时间,并且查询时对特殊字符%、_进行转义
 * @author 椰子皮
 */
@Slf4j
@Component
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class })
})

public class MybatisInterceptor implements Interceptor {
 @Override
    public Object intercept(Invocation invocation) throws Throwable {

        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
         Object parameter = invocation.getArgs()[1];
if (SqlCommandType.INSERT == sqlCommandType || SqlCommandType.UPDATE == sqlCommandType) {
            if (parameter == null) {
                return invocation.proceed();
            }
        Field[] fields = ConvertUtils.getAllFields(parameter);
            for (Field field : fields) {
                try {
                    if ("updateTime".equals(field.getName()) || "createTime".equals(field.getName())) {
                        field.setAccessible(true);
                        Object local_createDate = field.get(parameter);
                        field.setAccessible(false);
                        if (local_createDate == null || local_createDate.equals("")) {
                            field.setAccessible(true);
        //ConvertUtils.getNowTime() 为获取当前时间,此处省略其实现方式
      field.set(parameter, ConvertUtils.getNowTime());
                            field.setAccessible(false);
                        }
                    }
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
  }
            }
        } 
    //如果是查询
    else if (SqlCommandType.SELECT == sqlCommandType){
            Object[] args = invocation.getArgs();

        if (parameter instanceof MapperMethod.ParamMap) {
              Map<Object, Object> paramMap = (Map) parameter;
                Iterator<Map.Entry<Object, Object>> it = paramMap.entrySet().iterator();
                //遍历map,并且将string类型的参数进行转义
                while (it.hasNext()) {
                    Map.Entry<Object, Object> entry = it.next();
                    Object objectKey = entry.getKey();
                    if (objectKey instanceof String &&  entry.getValue() instanceof String) {
                          if ((StringUtils.isNotBlank(sql) && sql.contains("LIKE")) ||
                              (StringUtils.isNotBlank(sql) && sql.contains("like"))) {
                                         if (paramMap.containsKey("page") && 
                          sql.contains("COUNT")) {
                              String value = (String) entry.getValue();
                              entry.setValue(ConvertUtils.dealSpecChar(value));
                         }
                      }
                    } else if (objectKey instanceof String && ((String) objectKey).contains("param")){
                           if (paramMap.containsKey("page") && sql.contains("COUNT")) {
                          ConvertUtils.setSpecChar(entry.getValue());
                      } else if (!paramMap.containsKey("page")) {
                         ConvertUtils.setSpecChar(entry.getValue());
                        }
                    }
                }
            }

        }
        return invocation.proceed();
    }

    @Override
    public void setProperties(Properties properties) {
        System.out.println("...................");
    }

验证

根据测试大神的要求,当前按照设计思路已经编码完成,但是!咱不清楚到底能不能实现啊。还是得验证一下,测试大神的软件体验还是很重要的!

实践是检验真理的唯一标准!Just do it!

定义实体类TblCollectCamera(在需要搜索的字段cameraName设置定义好的注解@SpecChar)

/**
 *
 * @TableName tbl_collect_camera
 */
@TableName(value ="tbl_collect_camera")
@Data
public class TblCollectCamera implements Serializable  {
    /**
     * 主键
     */
    @TableId(type = IdType.AUTO)
    private String id;
 /**
     *
     */
    private String collectId;

    /**
     *
     */
    @SpecChar
    private String cameraName;

    /**
     *
     */
    private String resCode;

    /**
     *
     */
    private String updateTime;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;

}

编写DAO层TblCollectCameraMapper

/**
 * @author 椰子皮
 * @description 针对表【tbl_collect_camera】的数据库操作Mapper
 * @createDate 2022-12-05 14:45:45
 * @Entity generator.domain.TblCollectCamera
 */

public interface TblCollectCameraMapper extends BaseMapper<TblCollectCamera>{
 List<TblCollectCamera> getByCollectId(@Param("id") String id, @Param("cameraName") String cameraName);

    IPage<TblCollectCamera> getTblCollectCameraByPage(Page<TblCollectCamera> page, @Param("tblCollectCamera") TblCollectCamera tblCollectCamera);

}

可以看到此处两个方法,入参分别为实体类TblCollectCamera和字段cameraName,按照mybatis拦截器中的代码,cameraName字段如果不加注解其实也是可以被转义的,注解的目的是在入参为实体类时进行转义

调用方法:

 其中入参cameraName为特殊字符%

打断点观察:

可以看到mybatis拦截器拦截到了入参为一实体类tblCollectCamera,入参为cameraName=%,根据我们的代码判断,此会进入 else判断,即会使用反射+注解方式将带有@SpecChar注解的字段的参数进行转义

 if (objectKey instanceof String &&  entry.getValue() instanceof String) {
                        String value = (String) entry.getValue();
                        entry.setValue(ConvertUtils.dealSpecChar(value));
                    } else {
                          ConvertUtils.setSpecChar(entry.getValue());
                   }

继续断点往下走:

发现cameraName已经被转义了。查看查询结果:

发现不会查询到所有数据,验证OK。另外单个查询的话此处就不演示了,如果仅仅是@Param这种单个字段的查询模式,是不需要加注解就可以实现特殊字符串处理的。

最后

希望我司的测试能够会一些基本的sql和linux命令,别只当个体验官,那你离开了这个行业啥也不是。

最后感谢公司秦总的图片转发支持!

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值