肥宅钓鱼网
当前位置: 首页 钓鱼百科

简述mybatis中如何实现模糊查询(如何使用Mybatis的拦截器实现数据加密与解密)

时间:2023-08-12 作者: 小编 阅读量: 4 栏目名: 钓鱼百科

既然是拦截器,可以拦截哪些内容呢?试想一下......当程序写到持久层时,Mybatis会执行指定SQL语句,并处理请求参数和返回值。

拦截器介绍

Mybatis Interceptor 在 Mybatis 中被当作 Plugin(插件),不知道为什么,但确实是在 org.apache.ibatis.plugin 包下面。

既然是拦截器,可以拦截哪些内容呢?试想一下...... 当程序写到持久层时,Mybatis 会 执行 指定 SQL 语句,并处理 请求参数返回值。没错,Mybatis 拦截器可以帮助我们处理上述内容,请看官网的 Plugins 的片段, 内容不多

// 执行Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)// 请求参数处理ParameterHandler (getParameterObject, setParameters)// 返回结果集处理ResultSetHandler (handleResultSets, handleOutputParameters)// SQL语句构建StatementHandler (prepare, parameterize, batch, update, query)

拦截器的使用

如果需要实现自定义的拦截器,只需要实现 org.apache.ibatis.plugin.Interceptor 接口,该接口有三个方法:

Object intercept(Invocation invocation) throws Throwable;Object plugin(Object target);void setProperties(Properties properties);

我们要实现数据加密,进入数据库的字段不能是真实的数据,但是返回来的数据要真实可用,所以我们需要针对 Parameter 和 ResultSet 两种类型处理,同时为了更灵活的使用,我们需要自定义注解

自定义注解

类注解,将注解放在实体类上

/** * 需要加解密的类注解 */@Documented@Inherited@Target({ ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)public @interface EncryptDecryptClass {}

字段注解,将注解放在实体字段上

/** * 加密字段注解 */@Documented@Inherited@Target({ ElementType.FIELD })@Retention(RetentionPolicy.RUNTIME)public @interface EncryptDecryptField {}

有了这两个注解,我们可以在我们可以标记我们要处理的实体和实体中的字段

自定义参数处理拦截器

参考官网,通过 @Intercepts 和 @Signature 的联合使用,指定 ParameterHandler.Class 类型,同时通过 @Component注解注入到容器中,即可在设置参数的时候进行拦截,通过自定义接口 IEncryptDecrypt, 根据 Field 的各种类型自定义加密解密算法

@Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),})@ConditionalOnProperty(value = "domain.encrypt", havingValue = "true")@Component@Slf4jpublic class ParammeterInterceptor implements Interceptor { @Autowired private IEncryptDecrypt encryptDecrypt; @Override public Object intercept(Invocation invocation) throws Throwable { log.info("拦截器ParamInterceptor"); //拦截 ParameterHandler 的 setParameters 方法 动态设置参数 if (invocation.getTarget() instanceof ParameterHandler) { ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget(); PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0]; // 反射获取 BoundSql 对象,此对象包含生成的sql和sql的参数map映射 /*Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql"); boundSqlField.setAccessible(true); BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler);*/ // 反射获取 参数对像 Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject"); parameterField.setAccessible(true); Object parameterObject = parameterField.get(parameterHandler); if (Objects.nonNull(parameterObject)){ Class<?> parameterObjectClass = parameterObject.getClass(); EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(parameterObjectClass, EncryptDecryptClass.class); if (Objects.nonNull(encryptDecryptClass)){ Field[] declaredFields = parameterObjectClass.getDeclaredFields(); final Object encrypt = encryptDecrypt.encrypt(declaredFields, parameterObject); } } } return invocation.proceed(); } @Override public Object plugin(Object o) { return Plugin.wrap(o, this); } @Override public void setProperties(Properties properties) { }}

同样新建结果集拦截器

结果集拦截器

与参数拦截器基本一样, 只不过类型指定为 ResultSetHandler.class

@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args={Statement.class})})@ConditionalOnProperty(value = "domain.decrypt", havingValue = "true")@Component@Slf4jpublic class ResultInterceptor implements Interceptor { @Autowired private IEncryptDecrypt encryptDecrypt; @Override public Object intercept(Invocation invocation) throws Throwable { Object result = invocation.proceed(); if (Objects.isNull(result)){ return null; } if (result instanceof ArrayList) { ArrayList resultList = (ArrayList) result; if (CollectionUtils.isNotEmpty(resultList) && needToDecrypt(resultList.get(0))){ for (int i = 0; i < resultList.size(); i) { encryptDecrypt.decrypt(resultList.get(i)); } } }else { if (needToDecrypt(result)){ encryptDecrypt.decrypt(result); } } return result; } public boolean needToDecrypt(Object object){ Class<?> objectClass = object.getClass(); EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass, EncryptDecryptClass.class); if (Objects.nonNull(encryptDecryptClass)){ return true; } return false; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { }}

加密解密接口

IEncryptDecrypt 接口定义了 加密和解密两个方法:

public interface IEncryptDecrypt { /** * 加密方法 * @param declaredFields 反射bean成员变量 * @param parameterObject Mybatis入参 * @param <T> * @return */ public <T> T encrypt(Field[] declaredFields, T parameterObject) throws IllegalAccessException; /** * 解密方法 * @param result Mybatis 返回值,需要判断是否是ArrayList类型 * @param <T> * @return */ public <T> T decrypt(T result) throws IllegalAccessException;}

两个拦截器通过在 YAML 中配置属性,按条件注入,外加自定义加密解密算法,完成全局灵活的配置。

核心代码已上传至 Github Demo

问题彩蛋

也许应对当前的业务,看了该文章满足了当下需求,我们目前只看到了什么是 Mybatis 拦截器,怎样简单使用,拦截器的其他用法以及其他很多为什么都没有解决,关注公众号,回复“人迹罕至” 读完文章 「程序猿为什么要看源码」后 ,我不会满足眼前的这些基本应用,我会有诸多疑问,

  1. 我们日常写 CRUD 的业务,为什么 Executor 中只有 R(query) 和 U(update), 那么C(insert) 和 D(delete) 怎样处理的?
  2. 自定义拦截器是以什么方式被执行的,执行顺序是什么?
  3. 分页也是 Mybatis 拦截器的一种,带有分页的框架是怎样使用拦截器的呢?如 Mybatis Plus, PageHelper
  4. 虽然重写了 Inteceptor 接口的 public void setProperties(Properties properties) 方法,但是并没有写什么业务逻辑,这个方法能怎样使用?
  5. ......

后续文章也会通过读源码的方式逐步解析这些问题,当然你有相关问题也可以留言交流讨论

提高效率工具

依旧推荐在写文章时用到的高效工具,后续相关工具也会在文章中陆续更新,请持续关注

MyBatis Log Plugin

MyBatis Log Plugin 是 Intelligj IDEA 的一个插件,用来从 Mybatis 输出的 log 中提取出当前调用的 SQL 语句,并将参数封装在 SQL 语句中组成完整的 SQL,这样,当我们调试的时候更加清晰方便,可以轻松定位是否 SQL 又问题

转载自公众号:日拱一兵

,
    推荐阅读
  • 楼道感应灯有摄像头吗(楼道感应灯有摄像头吗)

    楼道感应灯没有摄像头的,楼道灯单灯采用感应灯,实现人来灯亮,人走灯灭,节能、降耗、使用方便,性能安全可靠,一次性投资长期免费使用,可满足住宅楼及办公楼的不同需要。摄像头是一种视频输入设备,属闭路电视的一种,被广泛的运用于视频会议,远程医疗及实时监控等方面。

  • 三国华容道所有摆阵方法(三国华容道横刀立马)

    横刀立马开局图华容道是我国传统智力玩具,有多种开局。棋盘上10枚棋子,大正方形曹操1枚,竖条五虎上将4枚,横条关羽1枚,小兵4枚。游戏的最终目的是用最少步数将曹操移到下方正中位置,逃出华容道。不允许跨越棋子,还要设法用最少的步数把曹操移到出口。棋盘下方有一个两方格边长的出口,是供曹操逃走的。棋盘上共摆有十个大小不一的棋子,它们分别代表曹操、张飞、赵云、马超、黄忠和关羽,还有四个卒。

  • 中专文凭可以升大专吗(中专毕业还能升大专吗有哪些方式呢来看看吧)

    中专升本有以下有五种方式供大家参考:第一种方式是参加对口高职考试。第二种方式是通过网络教育。自考没有入学考试,只需要参加相应的课程考试,所有课程考试合格后,符合毕业条件即可申请毕业证书。第四种方式是通过成年高考。成考需要参加全国统一考试,由各个省进行统一录取。第五种方式是通过电大。电大即中央广播电视大学以及地方各地广播电视大学。主要通过教材、多媒体课件等进行教学。电大的毕业证是中央电大发的。

  • 24w灯可以用48w驱动器吗(28w的驱动器能用在24w灯上吗)

    24w灯可以用48w驱动器。一般来说驱动器的功率比灯大可以,相反可能还更可靠,驱动器恒流输出的比较多,在恒定的电流输出时其输出电压是可以在一定范围内自动适应的,因此这样的驱动器可以直接配套不同的灯使用,但要注意驱动器的输出电压和电流跟灯的额定电压和电流要匹配,不然不能正常工作。驱动器从广义上指的是驱动某类设备的驱动硬件。

  • 治疗痔疮的最佳偏方(这些偏方有效治疗痔疮)

    偏方一蒲公黄、黄柏、赤芍、丹皮各30克,桃仁20克,土茯苓30克,白芷15克。本方为邹桃生方,功能清热解毒除湿消肿,凉血散瘀,适用于痔疮。偏方二硫黄、雄黄各10克,樟脑3克,麻油适量。前药研成细末,用麻油调匀,擦患处,一日三次。本方适用于湿热而致痔疮。水煎服,日1剂,日服2次。本方适用于痔疮抓破出血不流水者。本方为黄洪坤方,功能清肠利湿,止血,适用于痔疮。

  • 成都市2022年下半年高等教育自学考试考前公告(二)

    开考15分钟后考生不得进入考点。(六)其他未尽事宜,按照考点属地的最新疫情防控要求执行。考生务必认真学习,掌握相关内容,防止出现违规违纪行为。考试均安排在国家教育考试标准化考场,考生入场将进行安检,考生携带的手机等通讯工具须按考场要求集中统一管理;考场监控视频录像将全程回放;市无线电管理部门将对考点内及周边进行全方位的监控;省市区三级检查人员将对各考点进行巡查,严厉查处违纪作弊行为。

  • 照片失真是什么意思(照片失真介绍)

    以下内容希望对你有帮助!照片失真是什么意思照片失真是指图片在转存的过程中图像信息丢失,而产生的图像质量下降现象;失真又称“畸变”。指信号在传输过程中与原有信号或标准相比所发生的偏差,在理想的放大器中,输出波形除放大外,应与输入波形完全相同。

  • 手机上怎么压缩照片大小(手机上压缩照片大小的方法)

    手机上怎么压缩照片大小打开手机上的浏览器,在搜索框中输入图片压缩,点击下载一个在线网站,进入网站首页。在网站首页的右上方会出现菜单的符号,点击后得到导航列表,在列表中找到文档处理并点击,就会看到“图片压缩”,点击图片压缩进入到压缩的页面。进入图片压缩主页面后,点击上方蓝色框的“点击选择文件”,选择需要进行压缩的图片文件。

  • 为什么91助手不能用了(新增安卓设备省电优化)

    9月4日消息:91助手最近更新到了5.7.5.3011版,新增了安卓设备的省电优化功能,还有手机端百度连接助手自升级功能。91助手91助手5.7.5.3011更新内容:1.新增安卓设备的省电优化功能;2.解决手机版安装应用出现闪退修复的问题;3.新增手机端百度连接助手自升级功能;4.界面优化及BUG修复。91助手V5版本是一款通过电脑管理iPhone、iPad、AndroidPhone、AndroidPad等多种智能设备的PC端软件,支持苹果、安卓等智能手机操作系统。

  • 2022年三星堆博物馆青铜馆开放时间+门票

    三星堆博物馆青铜馆开放时间为8:30-18:00,普通票为72元/张,学生票为36元。