下划线键值转小写驼峰形式插件
有些人在使用 MyBatis 时,为了方便扩展而使用 Map 类型的返回值。使用 Map 作为返回值时,Map 中的键值就是查询结果中的列名,而列名一般都是大小写字母或者下画线形式,和 Java 中使用的驼峰形式不一致。而且由于不同数据库查询结果列的大小写也并不一致,因此为了保证在使用 Map 时的属性一致,可以对 Map 类型的结果进行特殊处理,即将不同格式的列名转换为 Java 中的驼峰形式。这种情况下,我们就可以使用拦截器,通过拦截 ResultSetHandler 接口中的 handleResultSets 方法去处理 Map 类型的结果。拦截器实现代码如下。
package tk.mybatis.simple.plugin;
import java.sql.Statement;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
/**
* MyBatis Map 类型下画线Key 转小写驼峰形式
*
* @author liuzenghui
*/
@Intercepts(
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
)
@SuppressWarnings({ "unchecked", "rawtypes" })
public class CameHumpInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//先执行得到结果,再对结果进行处理
List<Object> list = (List<Object>) invocation.proceed();
for(Object object : list){
//如果结果是 Map 类型,就对 Map 的 Key 进行转换
if(object instanceof Map){
processMap((Map)object);
} else {
break;
}
}
return list;
}
/**
* 处理 Map 类型
*
* @param map
*/
private void processMap(Map<String, Object> map) {
Set<String> keySet = new HashSet<String>(map.keySet());
for(String key : keySet){
//大写开头的会将整个字符串转换为小写,如果包含下划线也会处理为驼峰
if((key.charAt(0) >= 'A' && key.charAt(0) <= 'Z') || key.indexOf("_") >= 0){
Object value = map.get(key);
map.remove(key);
map.put(underlineToCamelhump(key), value);
}
}
}
/**
* 将下划线风格替换为驼峰风格
*
* @param inputString
* @return
*/
public static String underlineToCamelhump(String inputString) {
StringBuilder sb = new StringBuilder();
boolean nextUpperCase = false;
for (int i = 0; i < inputString.length(); i++) {
char c = inputString.charAt(i);
if(c == '_'){
if (sb.length() > 0) {
nextUpperCase = true;
}
} else {
if (nextUpperCase) {
sb.append(Character.toUpperCase(c));
nextUpperCase = false;
} else {
sb.append(Character.toLowerCase(c));
}
}
}
return sb.toString();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
这个插件的功能很简单,就是循环判断结果。如果是 Map 类型的结果,就对 Map 的 key 进行处理,处理时为了避免把已经是驼峰的值转换为纯小写,因此通过首字母是否为大写或是否包含下画线来判断(实际应用中要根据实际情况修改)。如果符合其中一个条件就转换为驼峰形式,删除对应的 key 值,使用新的 key 值来代替。当数据经过这个拦截器插件处理后,就可以保证在任何数据库中以 Map 作为结果值类型时,都有一致的 key 值,可以统一取值,尤其在 JSP 或者其他模板引擎中取值时,可以很方便地使用。想要使用该插件,需要在 mybatis-config.xml 中配置该插件。
<plugins>
<plugin interceptor="tk.mybatis.simple.plugin.CameHumpInterceptor" />
</plugins>
虽然这只是一个简单的例子,但是却有一段看似简单实际并不简单的代码,在上面拦截器代码的第 31 行,invocation.proceed() 执行的结果被强制转换为了 List 类型。这是因为拦截器接口 ResultSetHandler 的 handleResultSets 方法的返回值为 List 类型,所以才能在这里直接强制转换。如果不知道这一点,就很难处理这个返回值。许多接口方法的返回值类型都是 List,但是还有很多其他的类型,所以在写拦截器时,要根据被拦截的方法来确定返回值的类型。