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

简述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 又问题

转载自公众号:日拱一兵

,
    推荐阅读
  • 新疆南疆旅游注意事项(新疆南疆旅游注意事项有哪些)

    新疆南疆旅游注意事项“早穿皮袄午穿纱,围着火炉吃西瓜”是新疆气候典型的写照。如遇恶劣天气,气温乍暖乍寒,故请注意及时增减衣服,做好预寒及防暑工作。南疆部分地区如帕米尔高原海拔较高,紫外线照射强烈,如吐鲁番盆地夏季最高气温可达45度以上。南疆线路长,景点分散。在南疆旅游时部分景点须您下车行走、爬山或骑马,建议您最好在出发前准备一双舒适便于行走的户外鞋。

  • 猪肠衣是啥 猪肠衣是干什么用的

    猪肠衣是猪的新鲜小肠,经过加工除去肠内外的各种不需要的组织,剩下一层坚韧半透明的薄膜,称为肠衣。猪肠衣构成系网状纵横交错,故它的纵向拉力、横向拉力均较强,不会染色,可以作为香肠或腊肠外面的衣膜,既适用于手工灌肠,也适用于机器灌制。

  • 韩式田园风格特点大全(常见的风格田园风格等等)

    田园风格洛丽塔风格朋克风格最早朋克风格的典型装扮是用发胶胶起头发,穿一条窄身牛仔裤,加上一件不扣纽的白衬衣,再戴上一个耳机别在腰间,听着朋克的音乐进入上世纪九十年代以后,时装界出现了后朋克风潮,它的主要指标是鲜艳,破烂、简洁、金属OL风格OL是英文OFFICELADY的缩写。通常指上班族女性OL时装一般多数指套裙,很适合办公室穿着的

  • ap模式是什么意思(ap模式的含义)

    ap模式是什么意思AP是WirelessAccessPoint的简称,中文名称:无线接入点;AP模式也就是无线接入点模式。AP相当于一个连接有线网和无线网的桥梁,其主要作用是将各个无线网络客户端连接到一起,然后将无线网络接入以太网。ap模式的使用:当用户在宾馆、酒店、办公室以及其它一些场所,这些场所提供了一根网线供用户上网,用户把这根网线插在电脑上就可以直接上网的,无需其它设置。

  • 32根地址线内存多少 34根地址线内存多少

    内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。内存也被称为内存储器和主存储器,其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来,内存的运行也决定了计算机的稳定运行。

  • 饥肠辘辘怎么造句(饥肠辘辘的意思)

    战士们饥肠辘辘时,几位老大娘送来了大饼、鸡蛋和米汤。一见钟情的爱从来不会发生在饥肠辘辘的早餐以前。她连续玩了5小时的游戏后,饥肠辘辘的去找食物了。今日大鱼大肉,明日饥肠辘辘。有的孩子不吃早饭,中午回到家已饥肠辘辘,长此以往就会有损健康。现在已是午夜时分,所带食品早已用尽,虽有住处,用以歇脚,但饥肠辘辘,果腹无方。出自清·蒲松龄《聊斋志异·西湖主》。

  • 辽宁省博物馆儿童体验馆里有什么 辽宁省博物馆儿童体验馆里有什么项目

    辽宁省博物馆儿童体验馆儿童体验馆位于辽宁省博物馆展楼二层,面积近1000平方米,设有“石器王国”、“秦小开历险记”、“魔法衣橱”、“北方民族的家”、“手工坊”、“光影故事”、“漫‘话’兵器”、“阅读区”八个功能区域。是一个为小朋友们精心打造的历史文化乐园。

  • 日产最新骊威(日产骊威无畏登场)

    操作方便,档位个数为5。整体的耗油情况是5.8升/100公里。从造型的角度来看,整体车身很美观,车身结构为5门5座,车辆内饰优质。进气口设计较好,进气类型为自然吸气。动力方面,整体驱动力较好,驱动方式为前置前驱,最大功率为80.0kW。内饰方面,非常适合年轻人及家庭使用。对于一辆车来说除了传统的车内车外的配置一些亮点内容更值得我们考虑,该车的亮点信息有后倒车雷达热点推荐:王思聪抽奖收藏

  • 古人全麻(麻了胳膊的由来)

    真是佩服石智勇,一下子举起198公斤,竟然只是胳膊和手臂麻有点麻。所以,我们看到他一边举着一边跟裁判说:“麻了,胳(膊)(手)臂!

  • 波轮洗衣机简介 洗衣机波轮式

    波轮洗衣机的桶底装有一个圆盘波轮,上有凸出的筋。波轮式洗衣机是洗衣机的一种,由电动机带动波轮转动,衣物随水不断上下翻滚。洗涤衣物有单桶、套桶、双桶几种。优点是对衣物缠绕小,洗涤均匀损衣率低;洗涤缸缸体有全塑、搪瓷、铝合金、不锈钢四大类。波轮式洗衣机工作原理:依靠装在洗衣桶底部的波轮正反旋转,带动衣物上下左右不停地翻转,使衣物之间、衣物与桶壁之间,在水中进行柔和地摩擦,在洗涤剂的作用下实现去污清洗。