赞
踩
书接上回,在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;
- }
定义拦截器,在查询方法类型为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命令,别只当个体验官,那你离开了这个行业啥也不是。
最后感谢公司秦总的图片转发支持!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。