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

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

转载自公众号:日拱一兵

,
    推荐阅读
  • 抱子甘蓝怎么吃(小包心菜抱子甘蓝怎么吃)

    腌好以后把抱子甘蓝平铺在烤盘上。抱子甘蓝炒培根1、抱子甘蓝和培根放在一起烤症吃,味道特别好,在烤制时需要准备抱子甘蓝五百克,培根一百克,大蒜一头,植物油和食用盐各适量。

  • 手机看到满信号为什么网速很慢呢(手机明明是满格信号)

    如果你之前调整过APN,建议将其恢复默认设置。具体操作步骤,先打开手机设置,再找到"移动网络",然后选择移动数据,接着点击"接入点名称",再点击右上角的"三个点",最后选择"重置为默认设置"就行了。因为开启双卡4G功能后,副卡会不时的与网络进行信号交互,这有时候就会影响主卡的网络。

  • 郴州旅游必去十大景点(郴州十大必去景点介绍)

    苏仙岭史上就位居郴阳八景之首,自古享有“湘南胜地”的美称,并因苏仙神奇传说而驰名天下。郴州板梁古村(4A级)板梁古村,距今已有600多年历史,古村内至今仍保存了360多栋完好无损的明清历史建筑,被誉“湘南第一村”,也是郴州市唯一的“中国历史文化名村”。主要景点有25处,其中塔松傲雪、湘南风貌、石蛋生笋、艺术宝殿、水下晶锥堪称万华岩的五绝。

  • 兰州黄河铁桥始建于哪一年(兰州黄河铁桥究竟在何时)

    1929年6月1日,国民政府在南京举行的孙中山先生的葬礼,是为“奉安大典”。直到国民军北伐取得初步胜利后,人们才开始筹划迁葬仪式。在奉安大典前后,为纪念孙中山先生,全国各地掀起了修改地名以纪念中山先生的活动。已经控制甘肃的冯玉祥国民军也顺应这个潮流,修改了兰州地名纪念孙中山先生。走在兰州街头,不经意就会碰到以“中山”命名的地名。这都是为纪念中山先生而命名的,每个地名背后都有一段故事。

  • 佟雅丽名字打分117分

    文章目录:一、佟雅丽相关名字打分101二、佟雅丽相关名字评分98三、佟雅丽相关名字推荐四、佟雅丽相关名字大全五、其他人还看了一、佟雅丽相关名字打分101羊不奶陶登奎陈兆虎姚壮文李厚勇王善金佟南方佟承畴佟爱国邱承彬李孝佟培基许晓树佟读音佟士委李绍彬索条影杨维清高定秀储料仓佟雨妍高德美佟国民何承志庾家河富土康佟星佟卓尧高玉磊孔祥德佟悦佟掌柜张福兰乐靖宜白蛤蟆佟娅柳英文张剑锋佟佳乐活卉佟显生赖天生佟雅丽

  • 车前草和鱼腥草金钱草能一起吃吗(车前草和鱼腥草金钱草一起吃可以吗)

    都可以清热解毒,散瘀消肿,利湿退黄之功效,可用于热淋,沙淋,尿涩作痛,黄疸尿赤,痈肿疔疮,毒蛇咬伤,肝胆结石,尿路结石等症。鱼腥草味辛,性寒凉,归肺经。主要功效为清热解毒、消肿疗疮、利尿除湿、清热止痢、健胃消食。车前草味甘、淡;性微寒。归肺、肝、肾、膀胱经。主要用于治疗小便不利、淋浊带下、水肿胀满、暑湿泻痢、目赤障翳、痰热咳喘等。

  • 高职单招计算机类考试复习材料(高职单招计算机专业)

    以下内容希望对你有帮助!高职单招计算机类考试复习材料31、(单选)计算机最早的应用领域是()A.信息处理B.科学计算C.过程控制D.人工智能32、(单选)WORD文档的段落标记是在输入()之后产生的。

  • 哪些人是万万不可以吃竹笋的(不可以吃竹笋有哪些人)

    哪些人是万万不可以吃竹笋的发育期的儿童如果儿童吃太多笋子,容易缺钙,罹患软骨病,还会缺锌造成生长发育缓慢,这是因为笋中含的草酸会影响人体对钙和锌的吸收和利用,所以发育期间要特别注意不要多吃。患泌尿系结石者科学研究表明,竹笋中的草酸盐易与其他食物中的钙结合形成难以溶解的草酸钙,从而加重病情。肝硬化者经科学研究表明,有肝硬化的人一次进食较多竹笋,会导致上消化道出血,还会促使肝硬化加重,必须控制食用。

  • 正在服刑人员有权分得土地征用补偿款吗

    服刑人员被剥夺的是人身自由的权利或者政治权利,国家并未剥夺其获得合法财产的权利。农村村民因为犯罪被判刑后,在服刑期间依然为村集体经济组织的成员,所以,服刑人员应当享有与其他村民同等的待遇,不应以其为正在服刑的罪犯为由拒绝分配征地补偿款。

  • 每天吃多少蚕蛹合适(一天吃多少蚕蛹合适)

    蚕蛹营养丰富,能补充大量蛋白质,对人体非常有效。补充蛋白蚕蛹含有大量蛋白质,所以多吃蚕蛹就相当于补充蛋白质。我们需要的蛋白质可以通过吃蚕蛹来补充,所以许多人喜欢生产和吃蚕蛹是合理的,因为蚕蛹的营养价值很高。增强免疫力蚕蛹营养素能增强人体免疫力,使人体免疫细胞生长。这样,一旦免疫增强,其他疾病就会大大减少。壮阳蚕蛹的某些营养成分对男性性功能有良好的影响,具有补肾壮阳的作用。