mybatis 插件不生效原因排查
·
背景
在将mysql 迁移到达梦的过程中,有同事反馈达梦方法级别的插件失效。
插件实现原理:
-
在MyBatis调用Mapper方法时进行拦截
-
自动将原方法调用重定向到对应的DM版本方法
mybatis 两种插件,一种是executor,一种是stamentHandler.
我的插件代码如下:
package cn.com.cbim.task.config;
import cn.hutool.json.JSONUtil;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Intercepts({
@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})
})
@Component
@Slf4j
public class MethodInterceptor implements Interceptor {
@Value("${task.dbType:mysql}")
private String dbType; // 数据库类型配置
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this); // 包装目标对象
}
@Override
public void setProperties(Properties properties) {} // 设置属性方法
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("intercept-开始执行方法-" + invocation.getMethod().getName());
// 获取StatementHandler
Object[] args = invocation.getArgs(); // 方法参数
MappedStatement ms = (MappedStatement) args[0]; // 映射语句
String methodId = ms.getId(); // 方法ID
// 如果是DM数据库且方法名不带Dm后缀
if ("dm".equals(dbType) && !methodId.endsWith("DM")) {
try {
// 尝试查找对应的Dm方法
Configuration configuration = ms.getConfiguration();
String dmMethodId = methodId + "DM";
MappedStatement dmMs = configuration.getMappedStatement(dmMethodId);
// 如果找到Dm方法则替换执行
if (dmMs != null) {
log.info("intercept执行的方法是-" + dmMethodId);
args[0] = dmMs; // 替换为DM方法
}
} catch (Exception e) {
log.error("intercept-没有找到对应的Dm方法-继续执行原方法-或尝试使用sql替换-" + methodId,e);
}
}
Object proceed = invocation.proceed(); // 执行拦截方法
log.info(JSONUtil.parse(proceed).toStringPretty()); // 记录执行结果
return proceed;
}
}
我的是执行器插件不生效,以下以这个为例。
第一步:检查插件是否注册
package org.apache.ibatis.plugin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Clinton Begin
*/
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
如果未生效,则检查自己的插件方法是否遗漏重要的@Intercepts 注解或签名定义。生效则进行下一步。
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class YourInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 实现逻辑
return invocation.proceed();
}
}
第二步 在插件执行的方法中进行断点调试
org.apache.ibatis.plugin.Plugin
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
} catch (Exception var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
经过断点调试后发现,项目配置了MybatisPlusInterceptor 的拦截器,在这个拦截器中变更了默认方法。


是因为这个插件将默认方法进行扩展两个参数。 所以需要在我们拦截器的放上再添加上签名方法即可。如下图所示:

完整代码如下:
import cn.hutool.json.JSONUtil;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
// 定义拦截器签名
@Intercepts({
@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})
})
@Component
@Slf4j
public class MethodInterceptor implements Interceptor {
@Value("${task.dbType:mysql}")
private String dbType; // 数据库类型配置
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this); // 包装目标对象
}
@Override
public void setProperties(Properties properties) {} // 设置属性方法
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("intercept-开始执行方法-" + invocation.getMethod().getName());
// 获取StatementHandler
Object[] args = invocation.getArgs(); // 方法参数
MappedStatement ms = (MappedStatement) args[0]; // 映射语句
String methodId = ms.getId(); // 方法ID
// 如果是DM数据库且方法名不带Dm后缀
if ("dm".equals(dbType) && !methodId.endsWith("DM")) {
try {
// 尝试查找对应的Dm方法
Configuration configuration = ms.getConfiguration();
String dmMethodId = methodId + "DM";
MappedStatement dmMs = configuration.getMappedStatement(dmMethodId);
// 如果找到Dm方法则替换执行
if (dmMs != null) {
log.info("intercept执行的方法是-" + dmMethodId);
args[0] = dmMs; // 替换为DM方法
}
} catch (Exception e) {
log.error("intercept-没有找到对应的Dm方法-继续执行原方法-或尝试使用sql替换-" + methodId,e);
}
}
Object proceed = invocation.proceed(); // 执行拦截方法
log.info(JSONUtil.parse(proceed).toStringPretty()); // 记录执行结果
return proceed;
}
}
运行日志如下则说明生效了:

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)