mybatiesplus 用自定义注解实现取消指定方法结果集key驼峰化处理
问题
- 项目中出现部分持久层方法返回字段是动态的方法,返回类型为list
- 后端mybatiesplus开启了驼峰化处理,配置方式为map-underscore-to-camel-case: true,
在config中配置了
来开启全局驼峰化处理@Bean public ConfigurationCustomizer mybatisConfigurationCustomizer(){ return new ConfigurationCustomizer(){ @Override public void customize(MybatisConfiguration configuration){ configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory()); } }; }
版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
解决
- 在执行查询前会调用org.apache.ibatis.mapping.MappedStatement.getBoundSql(Object) 方法进行参数绑定等操作并返回 org.apache.ibatis.mapping.BoundSql的实例。
public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
this.additionalParameters = new HashMap();
this.metaParameters = configuration.newMetaObject(this.additionalParameters);
}
BoundSql中的成员变量 MetaObject metaParameters 为在构造方法中调用Configuration.newMetaObject 创建,
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper)object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map)object);
} else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection)object);
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
MetaObject 实例中的ObjectWrapper objectWrapper 成员变量为在config中配置的com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory
MybatisMapWrapperFactory的getWrapperFor方法会返回MybatisMapWrapper的实例,
public String findProperty(String name, boolean useCamelCaseMapping) {
return useCamelCaseMapping && !StringUtils.isCamel(name) ? StringUtils.underlineToCamel(name) : name;
}
MybatisMapWrapper 中的findProperty 方法进行驼峰化处理的方法。
由上可知,在前query前添加拦截器,调整configuration中的objectWrapperFactory 为默认的DefaultObjectWrapperFactory,或者自定义一个ObjectWrapperFactory和MapWrapper的实现类,实现不进行驼峰化处理。
- 通过自定义的注解来标注方法进行识别,在拦截器中进行调整
UseCamelCaseMapping
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface UseCamelCaseMapping {
boolean value() default true;
}
UseCamelCaseMappingInterceptor
import org.apache.commons.lang3.StringUtils;
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.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)
})
public class UseCamelCaseMappingInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
final Object target = invocation.getTarget();
final Object[] args = invocation.getArgs();
BoundSql boundSql = (BoundSql) args[5];
final Object parameter = args[1];
final MappedStatement mappedStatement = (MappedStatement) args[0];
final String namespace = mappedStatement.getId();
final String className = StringUtils.substringBeforeLast(namespace, ".");
final Class<?> clazz = Class.forName(className);
final String methodName = StringUtils.substringAfterLast(namespace, ".");
// 获得方法注解
final Method method = this.findMethod(clazz, methodName, parameter);
if (!method.isAnnotationPresent(UseCamelCaseMapping.class)) {
return invocation.proceed();
}
final UseCamelCaseMapping useCamelCaseMapping = method.getAnnotation(UseCamelCaseMapping.class);
final boolean value = useCamelCaseMapping.value();
//useCamelCaseMapping 默认为true与mybatiesplus默认保持一致,配置成false 时会取消驼峰化处理
if (!value) {
final Configuration configuration = mappedStatement.getConfiguration();
//这里将转换工厂设置为默认的,这样可以达到取消的目的
configuration.setObjectWrapperFactory(new DefaultObjectWrapperFactory());
//这里重新进行sql参数绑定会,结果处理会使用DefaultObjectWrapperFactory
boundSql = mappedStatement.getBoundSql(parameter);
args[5] = boundSql;
}
return invocation.proceed();
}
private Method findMethod(Class<?> clazz, String methodName, Object parameter) {
final Method[] methods = clazz.getMethods();
final List<Method> methodsList = new ArrayList<>();
for (int m = 0; m < methods.length; m++) {
final String menthod = methods[m].getName();
if (menthod.equals(methodName)) {
methodsList.add(methods[m]);
}
}
if (methodsList.size() == 1) {
return methodsList.get(0);
} else if (methodsList.size() > 1) {
return findMethodByParamater(methodsList, parameter);
} else {
return null;
}
}
private Method findMethodByParamater(List<Method> methodsList, Object parameter) {
Method result = null;
if (parameter instanceof Map) {
final Map parameterMap = (Map) parameter;
result = findMethodByParamaters(methodsList, parameterMap);
} else {
//这里就是
for (Method method : methodsList) {
final Parameter[] parameters = method.getParameters();
if (parameters.length == 1) {
final Class<?> type = parameters[0].getType();
if (type.equals(parameter.getClass()) || parameter.getClass().isAssignableFrom(type)) {
result = method;
break;
}
}
}
}
return result;
}
private Method findMethodByParamaters(List<Method> methodsList, Map parameterMap) {
Method result = null;
for (Method method : methodsList) {
final Parameter[] parameters = method.getParameters();
if (parameters.length == (parameterMap.size() / 2)) {
boolean flag = true;//
for (int i = 0; i < parameters.length; i++) {
final Class<?> type = parameters[i].getType();
final String name = parameters[i].getName();
final Class<?> valueClass = parameterMap.get("param" + (i + 1)).getClass();
//这里简单判断,不考虑高级根部类或接口做参数,判断逻辑太复杂,暂时没想好先搁置
if (!(parameterMap.containsKey(name) &&
(valueClass.equals(type) || valueClass.isAssignableFrom(type)))) {
flag = false;
break;
}
}
if (flag) {
result = method;
break;
}
}
}
return result;
}
@Override
public final Object plugin(Object target) {
return Interceptor.super.plugin(target);
}
@Override
public final void setProperties(Properties properties) {
Interceptor.super.setProperties(properties);
}
这里对于获取当前method的方法比较简单,对与重载方法只比对了参数和类型,对一些复杂的情况没有考虑,但在mapper中应该很少会重载方法。
参考:https://www.cnblogs.com/wftop1/p/17145655.html

浙公网安备 33010602011771号