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

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

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

既然是拦截器,可以拦截哪些内容呢?试想一下......当程序写到持久层时,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 又问题

转载自公众号:日拱一兵

,
    推荐阅读
  • 全球16个重要的海洋战略通道(世界六大海上重要通道)

    世界海运贸易额的7%都是通过苏伊士运河进行运送,而其运送的主要货物除了原油和石油产品,还有煤炭、矿石等金属材料和大量的谷物。不仅如此,苏伊士运河经过的全部船舶数量和货运数量在世界的国际运河中都能称得上是首位。土耳其海峡土耳其海峡全长为361千米,位于黑海和地中海之间,被称为“天下咽喉”。不仅在经济上,而且在军事,政治等多个方面,这些航线都有重大的战略地位。

  • 商标网上注册缴费通知单(商标注册网上服务系统今起全面上线运行)

    “随着我国一直以来大力推行商标注册全程电子化,昨日商标局官网发出公告,宣布商标网上服务系统拟定将于11月5日全面上线,届时商标注册的操作及流程将更为便捷。商标局官网截图通告全文如下按照商标注册便利化改革总体部署,我局积极推进商标注册全程电子化,努力打造更为优质、高效、便捷的商标注册网上服务平台。现将有关事项公告如下:商标网上服务系统拟于11月5日全面上线运行。届时,通过商标网上服务系统提交的商标申

  • 电视剧三十而已钟晓阳的扮演者(钟晓阳的扮演者是谁)

    电视剧三十而已钟晓阳的扮演者电视剧《三十而已》钟晓阳的扮演者是晏紫东。晏紫东,1994年12月10日出生于辽宁沈阳,中国内地男演员。2013年,晏紫东主演微电影《归》。11月出演网剧《降龙伏虎小济公》李逢春,同月发布《愉此一生》同名主题曲及同名MV;网络剧《识汝不识丁》主题曲《花开年少》特别版。2017年2月,发布《愉此一生》片尾曲及MV《思念飞花》,同月,参演历史风云剧《独步天下》。

  • 小学生的话怎么写(小学生的话写什么)

    小学生的话怎么写温暖的阳光照遍校园的每一个角落,小鸟的叫声传遍校园的每一个地方。在新学期里,我要把以前的“病”给治好,使自己成为一个“健康”的人。学会感恩,因为这会使世界更美好,使生活更加充实。我们需要一步一个脚印,踏踏实实地做学问,书山有路勤为径,学海无涯苦作舟。让我们共同探索未知世界,向着自己的目标,奋力前进!未来的宏伟蓝图需要我们来绘画,未来的世界是我们的,我们有理由学习努力。

  • 白对虾怎么做好吃(白对虾如何做才好吃)

    下面内容希望能帮助到你,我们来一起看看吧!白对虾怎么做好吃食材:白对虾400g,葱适量,姜适量,蒜适量,盐适量。用牙签除去虾线。然后将虾放入锅中,加水,放入葱段、姜片,大火烧开。水开后,放入适量的盐调味。盖上锅盖转为小火,继续煮5分钟。然后滴几滴食用油,拌匀即可。

  • 发财树的风水摆放位置 发财树的风水摆放位置图

    坐西向东的兑宅,财位在正南方、西北方、东南方;5、了解了家居财位之后,可将发财树放在这些风水位。风水位摆放发财树,可增加木行能量。盆景要保持健康茂盛,发财树是最好的选择。公司开业将发财树直接放在公司大门两边,财源滚滚来。

  • 秋季小班育儿知识小常识(幼儿园小班育儿知识有哪些)

    达到的是同样的甚至是更好的效果,所不同的只是你说话的方式而已,而孩子却更开心了,对你的“命令”同样是执行的。

  • angelababy和黄晓明近况(和Angelababy官宣离婚老好人黄晓明)

    2015年10月8日,黄晓明斥资2个亿,给杨颖举办了一场梦幻的世纪婚礼,几乎请来了大半个娱乐圈,风头甚至盖过了当时获得诺贝尔奖的屠呦呦。黄晓明和杨颖大婚婚礼上,黄晓明郑重承诺,虽然自己不是王子,但要把杨颖宠成公主。杨颖的年收入,一度反超黄晓明。去年1月,《乘风破浪的姐姐》第二季,黄晓明担任主持人,和前女友李菲儿的互动引起热议。而黄晓明的实力护妻,也打破了婚变传闻。黄晓明大受刺激,从此,开始勤学苦练。

  • 民字起名多少笔画好(给孩子起名姓是几画也要搞清楚)

    姓氏的笔画数一般依据的是《康熙字典》该字的写法,这是需要注意的。有的家长不清楚这一规则,给孩子起名时连姓的笔画数都搞错,为此。还有一些姓氏,虽然字跟现实的一样,但是与康熙字典的部首不同,笔画也就不相同。例如:黄:11画,康熙字典12画。郭:10画,康熙字典15画。

  • 泡椒辣不辣(新鲜青泡椒辣不辣)

    泡椒,俗称“鱼辣子”,是川菜中特有的调味料。泡椒具有色泽红亮、辣而不燥、辣中微酸的特点。泡椒分类:1、二荆条泡辣椒,这种辣椒相对较长,辣味适口,香气足,制作传统川菜鱼香肉丝就离不开它。