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

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

转载自公众号:日拱一兵

,
    推荐阅读
  • 安卓手机怎么样可以投屏到电视(安卓手机上的画面怎么投屏到电视上)

    第一步,首先我们在安卓手机以及家里的电视上,分别下载乐播投屏软件,然后进行软件安装。第二步,乐播投屏软件安装全都安装成功以后,我们将手机和电视连接到同一个WiFi网络下。第五步,接着手机的乐播投屏软件就会进入到界面当中,在此界面当中,我们打开功能,就可以将安卓手机上的画面投屏到电视上了。

  • DOTA2德鲁伊怎么出装 DOTA德鲁伊出装

    DOTA2德鲁伊怎么出装?德鲁伊是dota2里唯一一个能够算是两个英雄的英雄,出装选择极多,下面小编就带来DOTA2德鲁伊出装思路,一起来看看吧。DOTA2德鲁伊出装思路出门装,我会选择两把补刀斧+两个铁树枝干+两个仙灵火+小熊的TP,德鲁伊在3级前的攻击力是真的吃shi,这套出装应该是最好补刀的了。一定要给小熊买本TP,当小熊没血而1技能又cd时直接让小熊t回家补,这个很关键。

  • 欠多家银行信用卡钱还不上怎么办 欠多家银行信用卡逾期

    目前各家银行取现金都是要收取手续费。这样持卡人不仅要缴纳几十元的手续费,还要缴纳每天万分之五的透支利息。欠信用卡还不上的话一定要与银行协商还款,如果是恶意不还款的话会影响个人信用和征信;产生逾期利息费,一般是按照每天万分之五的利率进行计算的;被银行打电话催收;情节严重的可能还会被银行起诉或者是构成信用卡诈骗罪。

  • 夏字演变过程图文(你知道夏字的构成与意义吗)

    指一年的第二季,中国习惯指立夏到立秋的三个月时间,也指农历“四、五、六”三个月。因其有壮大、广大之义相与比述衍义:表示中国之人。《公羊传·成公十五年》:“春秋内其国而外诸夏。”中国历史上的第一王朝,系传说中禹的儿子启所建立,奴隶制国家,建都安邑,即夏后氏。《史记·货殖列传》:“虞夏以来。”。

  • 十二星座谁最能反抗(12星座谁能坐地吸土)

    十二星座谁最能反抗食物上的饥饿会导致整个人身体各项功能的紊乱,而精神上的饥渴也会造成人们心理上的变化以及行为上的异常。今天我们就来聊一聊,十二星座谁对爱情最饥渴吧!虽然工作的时候摩羯专一而又高冷,但是一旦和爱人单独在一起的时候,简直秒变黏糊的小奶狗,亲亲抱抱举高高,饥渴指数已然爆表

  • 纳豆的功效与作用禁忌(纳豆的功效与作用禁忌有哪些)

    纳豆的功效与作用禁忌是继山西侯马汉墓和湖南长沙市马王堆汉墓中出土文物大豆种子后的又一次关键发觉。纳豆的健康保健作用关键与在其中的纳豆激酶、纳豆大豆异黄酮、皂青素、维生素B122等多种多样作用系数相关。而据调查脑梗塞、心肌梗塞等各种各样静脉血栓病,病发时间多见早晨及星期一。因此直接生吃实际效果最好是;应该始终坚持每日吃:纳豆激酶进入体内后,其特异性保持12钟头。

  • 适合跨年的朋友圈句子(可以跨年发的句子)

    适合跨年的朋友圈句子常常责备自己的人,往往能得到他人的谅解。人生就像愤怒的小鸟,当你失败时,总有几只猪在笑。2022年第一天,你好。有些事不是看到了希望才去坚持,而是因为坚持了才会看到希望。淡忘仇恨,春暖花开。新的一年,新的一天,新的起点,只有对你的爱没有改变。害怕时间过得太快,但又期待它走得快一些。懦夫把困难举在头顶,英雄把困难踩在脚下。不要和别人攀比这样你会活的很轻松,人最大的敌人是自己。

  • 江边钓鲤鱼方法(钓鲫鱼的妙招)

    江边钓鲤鱼方法钓凹不钓平。据钓点岸边土质、水生植物状况来判断。另外,斜坡不可能有淤泥也缺少腐殖质物质,所以不要钓斜坡地方。鲤鱼白天多在游动中觅食,吃饱后会在深水区静卧一会,然后再窜游。因此,钓鲤鱼不能"守株待兔",而是要主动出击,可多打几个窝轮钓。这里所说的阴,是指水域中光照较差,有树荫、背阴处。春夏秋钓里钓阴,冬天在阳光照射的水域作钓点。甜与腥是指饵料的气味。鲤鱼荤素皆食,尤其喜食小麦。

  • 雅思考点仅剩13个(全国8个城市新增11个雅思机考考点)

    新增的11个机考考点分别位于北京、上海、杭州、长沙、郑州、福州、南昌、呼和浩特等城市。据悉,雅思机考于2018年在中国市场首次推出,截至今年9月,中国33个城市已有58个机考考点。目前,雅思机考一周可安排多个考试日期,一天内最多举办3场考试,考试前4天截止报名,考试结束后3天可以查询成绩。

  • 龟苓膏多久吃一次(龟苓膏多久吃一次合适)

    尤其是在月经期间,尽量不要吃或者吃太多。龟苓膏中含有多种活性多糖和氨基酸,虽然低热量、低脂肪、低胆固醇,能够调节血脂和血糖;可以当作减肥期间的补品,但它是不具备减肥作用的。如果只吃龟苓膏的话,不仅不能减肥,还很容易损害身体健康,损伤脾胃,甚至引起腹痛腹泻。补充人体所需要的氨基酸的成分。