Mybatis 之 Mybatis-Plus 插件
请结合上一边 >>> Mybatis 插件原理 <<< 进行查看
Mybatis 中自己定义了一个 自己的插件类接口 InnerInterceptor
其内部实现了一些现成的插件,
如 : PaginationInnerInterceptor 、租户插件: TenantLineInnerInterceptor (行及的,居于字段进行数据隔了)、乐观锁插件: OptimisticLockerInnerInterceptor 等等 ...
配置使用,使用过程中,将插添加(注册)到 服务中即可,注入到 IOC
* MP 新的配置插件配置 * 官网说明:@see { https://baomidou.com/pages/2976a3/#mybatisplusinterceptor } * * @return */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); /** * 乐观锁 */ interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); /** * 分页插件 */ interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); /** * 防止全表更新与删除 */ interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); /** * 数据权限范围处理插件(自定义的) */ interceptor.addInnerInterceptor(new DataScopeInterceptor()); /** * 表别名处理插件(自定义的) */ interceptor.addInnerInterceptor(new AliasInterceptor()); /** * 年月时间处理插件(自定义的)Java类 YearMonth ( yyyy-MM )与数据库 date ( yyyy-MM-dd ) 映射处理 */ interceptor.addInnerInterceptor(new YearMonthInterceptor()); log.info("########### Mybatis-Plus 插件装配完毕 ###########"); return interceptor; }
插件调用原理:(直接看源码)
@Intercepts( { @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}), @Signature(type = StatementHandler.class, method = "getBoundSql", args = {}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } ) public class MybatisPlusInterceptor implements Interceptor { // 配置中的所有的 MP 插件都注册到此集合中, @Setter private List<InnerInterceptor> interceptors = new ArrayList<>(); @Override public Object intercept(Invocation invocation) throws Throwable { // b被代理的目标对象 Object target = invocation.getTarget(); // 代理执行的参数 Object[] args = invocation.getArgs(); // 此插件同时监听了 StatementHandler 和 Executor 所以需要进行代理对象判断 if (target instanceof Executor) { // 执行的是 Executor 的逻辑 final Executor executor = (Executor) target; Object parameter = args[1]; // Executor 的 update 函数中只有两个参数,若参数length ==2 ,则断定为 update 逻辑 boolean isUpdate = args.length == 2; // Executor 的三个方法(update、query、query) 首个参数都是 MappedStatement 类型 MappedStatement ms = (MappedStatement) args[0]; // 执行的是 SELECT 语句 if (!isUpdate && ms.getSqlCommandType() == SqlCommandType.SELECT) { // Executor 的两个 query 方法中,第三个参数可以确定的 RowBounds 类 RowBounds rowBounds = (RowBounds) args[2]; // Executor 的两个 query 方法中,第四个参数可以确定的 ResultHandler 类 ResultHandler resultHandler = (ResultHandler) args[3]; BoundSql boundSql; if (args.length == 4) { // Executor 的 4个参数的那个 query 函数 boundSql = ms.getBoundSql(parameter); } else { // Executor 的 6个参数的那个 query,函数几乎不可能走进这里面,除非使用Executor的代理对象调用query[args[6]] boundSql = (BoundSql) args[5]; } // MP 自行封装的 InnerInterceptor 类集合,进行遍历调用插件逻辑 for (InnerInterceptor query : interceptors) { /** * 插件中进行判断是否需要执行 Executor.query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql) * 默认值为 true 为执行 */ if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) { // 返回空的,为不执行 return Collections.emptyList(); } // 执行查询的前置操作,可以对 SQL 进行一些修改 query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql); } // 进行一些缓存的操作逻辑 CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql); } else if (isUpdate) { // 执行的是 Executor 中的 update 方法,遍历执行 MP 中定义的注册的插件 for (InnerInterceptor update : interceptors) { /** * 判断是否执行 Executor.update(MappedStatement, Object) * 如果不执行update操作,则影响行数的值为 -1 */ if (!update.willDoUpdate(executor, ms, parameter)) { // 如果不执行update操作,则影响行数的值为 -1 return -1; } // 需要执行 Executor.update(MappedStatement, Object) update.beforeUpdate(executor, ms, parameter); } } } else { // 执行的是 StatementHandler 的逻辑 final StatementHandler sh = (StatementHandler) target; // 目前只有StatementHandler.getBoundSql方法args才为null if (null == args) { /** * 遍历执行 MP 封装的插件,没有参数,则执行的是 StatementHandler.getBoundSql() 函数 * 遍历执行 MP 中定义的注册的插件 */ for (InnerInterceptor innerInterceptor : interceptors) { innerInterceptor.beforeGetBoundSql(sh); } } else { /** * 有参数,则执行的是 StatementHandler.prepare(Connection connection, Integer transactionTimeout) */ Connection connections = (Connection) args[0]; // prepare 方法中两个参数的类型都是可以确定的 Integer transactionTimeout = (Integer) args[1]; // 遍历执行 MP 中定义的注册的插件 for (InnerInterceptor innerInterceptor : interceptors) { /** * StatementHandler.prepare(Connection, Integer) 操作前置处理 * 可以对 SQL 进行一些修改 */ innerInterceptor.beforePrepare(sh, connections, transactionTimeout); } } } // 调用代理执行目标方法,流程回归到 Mybatis 中 return invocation.proceed(); } @Override public Object plugin(Object target) { if (target instanceof Executor || target instanceof StatementHandler) { return Plugin.wrap(target, this); } return target; } public void addInnerInterceptor(InnerInterceptor innerInterceptor) { this.interceptors.add(innerInterceptor); } public List<InnerInterceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); } /** * 使用内部规则,拿分页插件举个栗子: * <p> * - key: "@page" ,value: "com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor" * - key: "page:limit" ,value: "100" * <p> * 解读1: key 以 "@" 开头定义了这是一个需要组装的 `InnerInterceptor`, 以 "page" 结尾表示别名 * value 是 `InnerInterceptor` 的具体的 class 全名 * 解读2: key 以上面定义的 "别名 + ':'" 开头指这个 `value` 是定义的该 `InnerInterceptor` 属性需要设置的值 * <p> * 如果这个 `InnerInterceptor` 不需要配置属性也要加别名 */ @Override public void setProperties(Properties properties) { PropertyMapper pm = PropertyMapper.newInstance(properties); Map<String, Properties> group = pm.group(StringPool.AT); group.forEach((k, v) -> { InnerInterceptor innerInterceptor = ClassUtils.newInstance(k); innerInterceptor.setProperties(v); addInnerInterceptor(innerInterceptor); }); } }
本文来自博客园,作者:Vermeer,转载请注明原文链接:https://www.cnblogs.com/chxlay/p/16837717.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步