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

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

转载自公众号:日拱一兵

,
    推荐阅读
  • 三年级观察作文(三年级观察作文范文)

    今天语文课是观察胖大海它一头尖,一头又圆摸上去很粗糙,像树皮一样我们又拿起它,闻了闻,上面还散发着淡淡的中药味呢,现在小编就来说说关于三年级观察作文?三年级观察作文今天语文课是观察胖大海。我们拿来了一个杯子,把胖大海放了进去,陈老师来到我们面前,把水倒到了我们的杯子里面。水会顺着时间而变得越来越黄,越来越黄。老师和我们说它可以润喉治咳嗽。

  • qq人被删了如何恢复(恢复好友步骤)

    以下内容希望对你有帮助!qq人被删了如何恢复在电脑主页面打开qq,登陆自己的账号进入主页面。在联系人的下方选择任何一个好友分组,然后右击,选择最下方的“好友管理器”。进入“好友管理器”,点击左下角的“找回被删的好友”,那一行字比较小。接着会进入网页,我们点击主页面的恢复qq好友或者恢复qq群。普通用户最多恢复3个月内的被删好友,会员最高一年以内。

  • 老人按键手机怎么调情景模式(老人按键手机如何调情景模式)

    老人按键手机怎么调情景模式?一般设置方法如下进入手机系统设置,选择情景模式,我来为大家讲解一下关于老人按键手机怎么调情景模式?跟着小编一起来看一看吧!在“情景模式界面”,选择需要的模式,如果需要调节音量,选择点击“标准”右侧的设置图标,在“标准”设置界面,点击选择“音量”,可调节铃声和通知的音量大小。最后点击“确定”即可。

  • 攻城掠地怎么获得借兵次数(攻城掠地兵力上万怎么弄的)

    这里一共有五瓶酒,其中金色四级兵书,红绿就被3级兵书,白蓝酒杯两级兵书,碰到酒逢知己以后兵书可以加2级。

  • 成都地铁已经开通了几条线路(成都8条新地铁线开通试运营时间表)

    210号线三期工程站名图,仅供参考10号线三期工程,起于人民公园站,止于太平园站(不含)。417号线二期工程站名图,仅供参考17号线二期工程,起于机投桥站(不含),止于高洪站。727号线一期工程站名图,仅供参考27号线一期工程,起于石佛站,止于蜀鑫路站。线路全长约24.86km,共设车站23座,其中地下站17座,高架站6座,设大丰车辆基地1座,主变电所2座。线路全长约27.74km,共设车站24座,全部为地下线。

  • 车载播放器测评(这款车载播放器不以情怀打动你)

    最近,福建歌航电子正式推出了旗下DSD云主机相配套的带屏幕显示的第三代车载主机控制器。作为一个切入到车载HiFi音响的歌航,这几年不断地优化歌航DSD云主机播放器,从和原车电子的适配性、安全驾乘的操控功能,到车载音源的Hi-end级无损播放,可以说正在全面超越传统的国际大牌。

  • 梦见别人杀人见血(梦见别人杀人见血好不好)

    我们一起去了解并探讨一下这个问题吧!梦见别人杀人见血梦见别人杀人见血,很多人会认为这肯定是个凶兆。我们要知道的是梦见血,吉兆,血意味着会发财。梦见看见别人杀人主大吉。另外梦见别人杀人,只是象征梦者受到了外来的影响,自己的一部分性格无法继续存活。别人杀了人,自己在场,或又是别人来杀,表示梦者试图摆脱那人施加于你的影响。

  • 济宁出租车驾驶员上岗模拟考试平台(济宁出租车上岗证考试题库)

    全国公共科目考试题库和区域科目考试题库均向社会公开。区域科目考试题库(含试题答案)由各城市出租汽车行政主管部门在部门政务网站或从业资格考试报名网站公开。

  • 酸菜豆腐怎么做(做酸菜豆腐的步骤)

    下面希望有你要的答案,我们一起来看看吧!酸菜豆腐怎么做准备酸菜和一块老豆腐把酸菜切碎。豆腐切成小方块。平底锅中放食用油适量,再放入豆腐块小火煎至两面金黄。豆腐块煎至两面金黄后装盘备用。炒锅中放油适量,再放入花椒粒和姜蒜末炒出香味。放入酸菜炒2至3分钟。加一小勺剁椒与酸菜一起翻炒匀。水开之后放入豆腐块。盖上锅盖小火焖煮10分钟。十分钟后放入鸡精和葱花翻炒均匀即可。

  • 楼顶的悬疑民间故事(民间故事之青岛诡楼)

    楼顶的悬疑民间故事在青岛的南京路26号,矗立着一栋看起来再正常不过的居民楼,但到了晚上却是一片漆黑,和楼下灯火通明的商户形成了鲜明的对比。位于南京路与江西路叉口的东南处是一栋五层居民楼,所处地断也属于繁华地带,但是在后来传出闹鬼传闻后,每当夜幕降临,楼上总是漆黑一片。一行人来到传出哭声的房间后,由于大门紧锁,只能透过窗户观察里面的情况。