前言
最近在考虑写什么的时,想到自己在项目中使用过的mybatis的插件,就想趁这个机会聊一聊我们接触频繁的Mybatis.
如果是使用过Mybatis的小伙伴,那么我们接触过的第一个Mybatis的插件自然就是分页插件(Mybatis-PageHelper)啦。
你有了解过它是如何实现的吗?你有没有自己编写 Mybatis 插件去实现一些自定义需求呢?
插件是一种常见的扩展方式,大多数开源框架也都支持用户通过添加自定义插件的方式来扩展或改变框架原有的功能。
Mybatis 中也提供了插件的功能,虽然叫插件,但是实际上是通过拦截器( Interceptor )实现的,通过拦截某些方法的调用,在执行目标逻辑之前插入我们自己的逻辑实现。另外在 MyBatis 的插件模块中还涉及责任链模式和 JDK 动态代理~
文章大纲:
一、应用场景
- 一些字段的自动填充
- SQL语句监控、打印、数据权限等
- 数据加解密操作、数据脱敏操作
- 分页插件
- 参数、结果集的类型转换
这些都是一些可以使用Mybatis插件实现的场景,当然也可以使用其他的方式来实现,只不过拦截的地方不一样罢了,有早有晚。
二、Mybatis实现自定义拦截器
我们用自定义拦截器实现一个相对简单的需求,在大多数表设计中,都会有create_time和update_time
等字段,在创建或更新时需要更新相关字段。
如果是使用过MybatisPlus
的小伙伴,可能知道在MybatisPlus
中有一个自动填充功能,通过实现MetaObjectHandler
接口中的方法来进行实现(主要的实现代码在com.baomidou.mybatisplus.core.MybatisParameterHandler
中).
但使用Mybatis
,并没有相关的方法或 API 可以直接来实现。所以我们这次就用以此处作为切入点,自定义拦截器来实现类似的自动填充功能。
编写步骤
- 编写一个拦截器类实现 Interceptor 接口
- 添加拦截注解 @Intercepts
- 在xml文件中配置拦截器或者添加到Configuration中
基础的环境我就不再贴出来啦哈,直接上三个步骤的代码
2.1、编写拦截器
typescript
复制代码
package com.nzc.interceptor; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Signature; import org.springframework.beans.factory.annotation.Value; import java.lang.reflect.Field; import java.util.*; /** * @author 宁在春 * @version 1.0 * @description: 通过实现拦截器来实现部分字段的自动填充功能 * @date 2023/4/6 21:49 */ @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) }) @Slf4j public class MybatisMetaInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; String sqlId = mappedStatement.getId(); log.info("------sqlId------" + sqlId); SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); Object parameter = invocation.getArgs()[1]; log.info("------sqlCommandType------" + sqlCommandType); log.info("拦截查询请求 Executor#update 方法" + invocation.getMethod()); if (parameter == null) { return invocation.proceed(); } if (SqlCommandType.INSERT == sqlCommandType) { Field[] fields = getAllFields(parameter); for (Field field : fields) { log.info("------field.name------" + field.getName()); try { // 注入创建时间 if ("createTime".equals(field.getName())) { field.setAccessible(true); Object local_createDate = field.get(parameter); field.setAccessible(false); if (local_createDate == null || local_createDate.equals("")) { field.setAccessible(true); field.set(parameter, new Date()); field.setAccessible(false); } } } catch (Exception e) { } } } if (SqlCommandType.UPDATE == sqlCommandType) { Field[] fields = getAllFields(parameter); for (Field field : fields) { log.info("------field.name------" + field.getName()); try { if ("updateTime".equals(field.getName())) { field.setAccessible(true); field.set(parameter, new Date()); field.setAccessible(false); } } catch (Exception e) { e.printStackTrace(); } } } return invocation.proceed(); } @