书接上回,在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 {@Overridepublic 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")) {String value = (String) entry.getValue();entry.setValue(ConvertUtils.dealSpecChar(value));}} else {//key值不是string,即可能是个实体类,统一按照注解处理ConvertUtils.setSpecChar(entry.getValue());}}}}return invocation.proceed();}@Overridepublic 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;/****/@SpecCharprivate 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命令,别只当个体验官,那你离开了这个行业啥也不是。

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

Mybatis Plugin拦截器处理特殊字符串相关推荐

  1. Mybatis 通过拦截器动态修改SQL

    01 使用场景 当我们在多租户的项目中,编写SQL语句都要带上tenant字段,用于区分不同的租户只能操作自己的数据. 比如,像下面的SQL select * from member where id ...

  2. Mybatis Interceptor 拦截器原理 源码分析

    Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最 ...

  3. mybatis使用拦截器显示sql,使用druid配置连接信息

    mybatis使用拦截器显示sql,使用druid配置连接信息 mybatis sql Druid 1.显示出sql内容: 新建2个类: MybatisInterceptor :拦截sql,并获得输出 ...

  4. mybatis 自定义拦截器

    拦截器注解 mybatis自定义拦截器实现步骤: 实现org.apache.ibatis.plugin.Interceptor接口. 添加拦截器注解org.apache.ibatis.plugin.I ...

  5. 一步步教你mybatis分页,mybatis分页拦截器 使用,mybatis拦截器分页

              mybatis 分页详解.mybatis分页查询,mybatis分页拦截器使用.struts2下mybatis分页 mybatis默认是支持分页的,内部通过创建可滚动的Result ...

  6. mybatis redis_基于人事年假管理的系统springboot+mybatis+redis+拦截器

    那么分享一个项目案例篇吧! 需求是:假设有一个部门表,一个员工表,一个年假表, 员工表里: 部门表: 休假表: 管理员表     项目原型: 效果图: 管理员登录: 登录成功回调: 进入后台首页,这里 ...

  7. 如何使用Mybatis的拦截器实现数据加密与解密

    点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 转载自公众号:日拱一兵 关注我,回复口令获取可获取独家整理的学习资料: - 001 :领取<Sp ...

  8. Mybatis SQL拦截器实现

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文原文阅读:https:/ ...

  9. MyBatis框架 拦截器简单使用

    Interceptor 是MyBatis提供的一个插件(plugin扩展).代表拦截器,可以拦截代码中的数据库访问操作,即Statement操作 拦截后,可以去修改正在执行的SQL语句,可以额外访问数 ...

最新文章

  1. 股票移动平均线matlab,股票的移动平均线 (图文)
  2. 高处看Surface,WIndow,View,SurfaceView
  3. echart的进阶使用(option)
  4. go oracle编程,go基础编程(一):第一个go程序-hello word
  5. 艾伟:WCF从理论到实践(2):决战紫禁之巅
  6. 人类为什么没有尾巴?这个跳跃基因抹去了人类的尾巴,并带来了额外风险
  7. “忘恩负义”的浪胃仙,是个真狠人!
  8. 图像中的一条直线在旋转缩放过程中会有分段现象,或产生分段节点
  9. 黑马程序员传智播客 匹配分组学习分组
  10. 超宽带 DWM1000模块 电气规格
  11. java零基础到精通全套视频教程
  12. 计算机网络结构示意图,常见的五种计算机网络拓扑结构分析
  13. 视频直播微信小程序开发方案
  14. Spring: error at ::0 can‘t find referenced pointcut的错误并解决
  15. Androi实现三个页面跳转
  16. 记使用腾讯TIM开发聊天通讯遇到的问题及解决方案
  17. 升余弦和根升余弦滤波器(SRRC,RRC)的单位脉冲响应
  18. python米和厘米转换代码_用Python写一个朴素的长度单位转换器
  19. hdu5594 ZYB's Prime
  20. springmvc对json数据的处理

热门文章

  1. 数据可视化如何实现?4大基本流程了解一下
  2. awk sed grep find sort常用配搭用法
  3. Scratch编程 烧脑算法——换位密码
  4. 浏览器兼容问题,一直是自己最头疼,一直回避的问题,今天看得到了一些启发,发奋今天开始研究这个,哈哈,以下为转载文章,好好学习。...
  5. Wifi驱动开发-学习笔记(一)
  6. 应对当今的医疗器械软件测试开发挑战,如何选择测试软件
  7. docker启动容器指定主机名,网络和ip地址
  8. 5.雅思口语——别再用delicious food啦
  9. OpenLayers画点、画圆、画线方法
  10. 50道必备的Python面试题 (建议点赞)