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

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

转载自公众号:日拱一兵

,
    推荐阅读
  • 下一站江湖支线攻略(攻略交给你)

    下一站江湖支线攻略田羽:在天机阁玄天宗附近有个行人,对话后找到田羽。陵城员外的夜明珠:地图上找到年迈的老人得知消息,北城进医馆触发傅梦之剧情,然后去的酒楼,对话右边侠客,然后去对话侠客后获得图纸神龙再造膏。秦二:在快刀门附近遇到秦二,选择帮他后去快刀门那里,也就是大盗那,打一架后获得玉佩,还可以进去再打一次,打完去陵城衙门可换取5000金钱。

  • 海南自驾游攻略(海南西线自驾游攻略)

    海南自驾游攻略永庆寺:门票50元,从海口出发50分钟可到,琼北规模最大的佛教禅林圣地!穿过寺院就是黄金海岸,佛门圣地前的碧海蓝天,身心更加宁静。晚上可落宿临高县城。东坡书院:门票20元,从临高出发1小时可到。是我国保存最完好的原始日晒制盐厂。海南唯一保留原始天然景观的海湾,人少景美,水清见底,沙细质软,洁白如银。

  • 能量保护罩在哪里 能量保护罩在哪里兑换

    支付宝公司从2004年建立开始,始终以“信任”作为产品和服务的核心。旗下有“支付宝”与“支付宝钱包”两个独立品牌。支付宝与国内外180多家银行以及VISA、MasterCard国际组织等机构建立战略合作关系,成为金融机构在电子支付领域最为信任的合作伙伴。2020年2月,尼泊尔央行向支付宝颁发牌照。

  • 圆形甜大馍怎么做(圆形甜大馍做法介绍)

    主料:面粉100g辅料:白糖20g、酵母1g,我来为大家讲解一下关于圆形甜大馍怎么做?圆形甜大馍怎么做主料:面粉100g。将白糖和酵母放入一个小碗当中,用35℃左右的水溶解后,静置10分钟左右。盖上湿布,放温暖处发酵。发酵好的面团,搓揉,排气,整形。用刀切成差不多大小的小剂子,将小剂子整成圆形,放在铺好湿纱布的蒸屉上饧10分钟。将蒸屉放在冷水锅里,大火上汽后15分钟关火,关火后,继续焖5分钟左右,即可开盖食用。

  • 美食推荐清炒小油菜(清炒油菜鲜香滋味)

    By凌宇飞扬123用料油菜500克葱1段姜2片花生油20克热炒鲜露2勺花椒10粒盐2克做法步骤1、油菜去根去老叶洗净,切好2、锅中放油加入葱、姜和花椒爆香3、倒入油菜快速翻炒变软4、加入热炒鲜露翻炒均匀5、加入盐翻炒均匀即可6、7、小贴士油菜很易熟,稍变软即可,炒时间太长维生素极易丢失。----------豆果美食,家庭厨房领域最大的美食社区及交易平台;85万道美食菜谱,任您挑选,可在各大应用市场下载App查看。

  • 女生练背部肌肉的方法(女生练背部肌肉的方法简述)

    向人体方位拖动弯举,手臂接近躯体,随后再挺直手臂下降杠铃。做轻艇划艇,要让胳膊到侧面,以后让手向前,再立即拉下去,做3组,每一组8次,三个方位加起來划算1次。

  • 女朋友难过安慰的话 女朋友难过安慰的话语

    连上帝都认为伤心和烦恼是不属于你的。努力让自己走出来,你会感到今天的太阳比昨天更温暖!把烦人的事慢慢地淡忘,让痛苦烦闷逐渐减弱、消退,相信你的心情会变得开朗起来。

  • 排除发动机怠速不稳的原因(发动机怠速不稳的原因和诊断办法)

    也可以说,凡是引起发动机汽缸内气体作用力变化的故障都有可能导致发动机怠速抖动。自动变速器、空调、转向助力器有故障会增加怠速负荷,引起怠速不稳。气门工作面与气门座圈积炭过多,气门密封不严,使各汽缸压缩压力不一致。气门相关件有故障,如气门推杆磨损或弯曲,摇臂磨损,气门卡住或漏气,气门弹簧折断等。

  • 珍珠油杏与荷兰香蜜杏比较(如何区别珍珠油杏与荷兰香蜜杏)

    跟着小编一起来看一看吧!珍珠油杏与荷兰香蜜杏比较珍珠油杏于6月中旬成熟,平均单果重26.3克,最大果重38克。果肉橙黄色,韧、硬,味浓甜,香甜清香,品质上乘。荷兰香蜜杏在6月底7月初成熟,属于晚熟品种,平均单果重量65-70克。口感清脆爽口,白熟期即可食,脆甜不酸。

  • 黄柏盆栽方法(怎么盆栽黄柏比较好)

    黄柏盆栽方法黄柏制作盆景可以采用扦插的方法繁殖,也可以采用分株的方法来繁殖,扦插法繁殖可以采用硬枝扦插也可以采用嫩枝扦插,硬枝扦插一般在每年的早春3月时进行,选择1年生的健壮没有病害的枝条作为插条,长度为20厘米左右就可以了,扦插以后放在半阴的地方进行养护,保持土壤湿润。