前言:项目使用springboot+mybatis搭建,在一些联表查询中使用了Map集合来接收结果,不过发现返回的结果中key都是和数据库列名一样:带有下划线很不方便,记得官方文档上有个驼峰转换的配置 map-underscore-to-camel-case , 于是想当然的以为加上就ok了,测试后发现接收为对象实体的时候确实是可以转换为驼峰形式,但是map集合还是不起作用。
于是想到从源码入手,先理清 map-underscore-to-camel-case是怎么运行的,首先在配置文件中增加如上配置,然后点击进入源码发现mybatis将配置文件中的值读取赋值给自己的属性mapUnderscoreToCamelCase:
然后点击属性mapUnderscoreToCamelCase,查看有哪些地方进行了引用,发现有个DefaultResultSetHandler.java调用MetaObject中的findProperty方法使用到了这个属性:
接着点击 findProperty 方法,发现 ObjectWrapper 是一个接口,这样我们就需要查看是哪个类进行了实现,通过 debug 发现是MapWrapper 和 BeanWrapper 两个类进行了实现,前者没有做任何处理,直接返回了name,后者判断如果配置为true的话将返回的列名toUppercase后和通过反射获得的对象属性进行比对,如果比对上了就返回属性名(这也就解释了为什么结果为实体bean的时候会进行转换,map没有转换的原因):
BeanWrapper.java
@Override
public String findProperty(String name, boolean useCamelCaseMapping) {
return metaClass.findProperty(name, useCamelCaseMapping);
}
MetaClass.java
public String findProperty(String name, boolean useCamelCaseMapping) {
if (useCamelCaseMapping) {
name = name.replace("_", "");
}
return findProperty(name);
}
public String findProperty(String name) {
StringBuilder prop = buildProperty(name, new StringBuilder());
return prop.length() > 0 ? prop.toString() : null;
}
private StringBuilder buildProperty(String name, StringBuilder builder) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
String propertyName = reflector.findPropertyName(prop.getName());
if (propertyName != null) {
builder.append(propertyName);
builder.append(".");
MetaClass metaProp = metaClassForProperty(propertyName);
metaProp.buildProperty(prop.getChildren(), builder);
}
} else {
String propertyName = reflector.findPropertyName(name);
if (propertyName != null) {
builder.append(propertyName);
}
}
return builder;
}
Reflector.java
/**
*在初始化Reflector的时候根据反射机制将指定class的所有属性获取并全部转成大写保存到下面的caseInsensitivePropertyMap集合里,这里进行get操作并返回
*/
public String findPropertyName(String name) {
return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH));
}
看到这里已经很清楚怎么改了:
1、我们需要自定义 wrappe r继承 MapWrapper 类并重写它的 findProperty 方法,通过userCamelCaseMapping参数判断是否需要进行驼峰处理。
package com.sailing.yjbj.config.wrapper;
import com.google.common.base.CaseFormat;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.MapWrapper;
import java.util.Map;
/**
* @author baibing
* @project: iemp-yjbj
* @package: com.sailing.yjbj.config.wrapper
* @Description: 自定义wrapper处理spring boot + mybatis返回结果为map时的key值转换为驼峰
* @date 2018/11/1 11:34
*/
public class CustomWrapper extends MapWrapper {
public CustomWrapper(MetaObject metaObject, Map<String, Object> map) {
super(metaObject, map);
}
@Override
public String findProperty(String name, boolean useCamelCaseMapping) {
if(useCamelCaseMapping){
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL,name);
}
return name;
}
}
2、同时也要实现 ObjectWrapperFactory 工厂接口,判断如果所传对象为Map类型的时候,才将自定义的 wrapper 处理器返回并使用。
package com.sailing.yjbj.config.wrapper;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import java.util.Map;
/**
* @author baibing
* @project: iemp-yjbj
* @package: com.sailing.yjbj.config.wrapper
* @Description: 实现接口 ObjectWrapperFactory,通过包装工厂来创建自定义的wrapper
* @date 2018/11/1 11:38
*/
public class MapWrapperFactory implements ObjectWrapperFactory{
@Override
public boolean hasWrapperFor(Object object) {
return object != null && object instanceof Map;
}
@Override
public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
return new CustomWrapper(metaObject,(Map)object);
}
}
3、最后需要替换原来默认的实现,mybaits文档上告诉了我们怎么做,返回一个 ConfigurationCustomizer ,通过匿名内部类将自己的实现工厂set进去即可:
package com.sailing.yjbj.config;
import com.sailing.yjbj.config.wrapper.MapWrapperFactory;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author baibing
* @project: iemp-yjbj
* @package: com.sailing.yjbj.config
* @Description: mybatis配置类,将自定义的MapWrapperFactory覆盖默认的ObjectWrapperFactory
* @date 2018/11/1 11:42
*/
@Configuration
public class MybatisConfig {
@Bean
public ConfigurationCustomizer mybatisConfigurationCustomizer(){
System.out.println("initiazing ConfigurationCustomizer....");
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setObjectWrapperFactory(new MapWrapperFactory());
}
};
}
}
4、驼峰转换的时候使用了google的guava库,maven依赖为:
<!-- 引用的 guava库,里面有转换驼峰的-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>24.1-jre</version>
</dependency>
最后:通过测试返回的 Map 的时候成功进行了驼峰处理~~~~
项目下载地址:https://download.csdn.net/download/white_ice/10778916
因csdn后台会自动修改积分,导致现在积分较高,可以去下面地址免费下载:https://github.com/KingOfMonkey/springboot-mybatis ,喜欢的给个star。