前言
程序员使用Mybatis的时候,通常会加一个驼峰配置,如下:
mybatis:
configuration:
map-underscore-to-camel-case: true
驼峰约定
什么是驼峰写法?简单来说,是java ORM对象中,增加可读性的一种约定俗成的写法;一个字段如果由多个单词组成,除第一个单词以外,后续单词都用大写开头。比如图片地址,我们一般写成:imageUrl。
在最终源码解释的例子中,我们将数据结果集中的个性签名字段别名设置成了【Per_son_Al_Signat_ure】,是否可以正确映射java ORM对象字段呢?
在Mybatis中的使用
返回java对象
public class UserInfoResponse {
/**
* 个性签名
*/
private String personalSignature;
/**
* 昵称
*/
private String nickname;
/**
* 头像图片url
*/
private String avatarUrl;
/**
* 忽略后续字段
*/
}
@Select("select nickname ,avatar_url ,personal_signature" +
" from tb_user " +
" where user_id = #{userId} ")
UserInfoResponse userInfo(@Param("userId") Integer userId);
在这个查询方法中,可以看到avatar_url、personal_signature与java对象中的属性拼写不是一一对应的,mybatis的驼峰配置可以将这个查询结果集映射到UserInfoResponse对象中。
什么情况下失效
以一个属性为例,personal_signature,如果将数据库字段别名拼写成tm.personal_signature as personalSignature,是没有问题的,可以正确返回;如果别名写成per_son_al_signat_ure或者p_signature呢?
经过实践,写成per_son_al_signat_ure也是可以的,但是如果删减了字符,比如p_signature,查询语句不会出异常,返回对象中personalSignature属性不会进行值映射,返回null。
也就是说,除下划线以外,删减字符会使值映射失效。
源码解释
- 在DefaultResultSetHandler类的createAutomaticMappings方法,引入了驼峰配置。
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase()); - 真正使用驼峰配置的地方【MetaClass类】,可以看到,如果设置了驼峰参数,则只是将属性中的下划线都替换成了空字符,而没有处理大小写。那为什么别名设置成personalSignature也可以生效呢?
public String findProperty(String name, boolean useCamelCaseMapping) {
if (useCamelCaseMapping) {
name = name.replace("_", "");
}
return findProperty(name);
}
- 与第二步同一个类的buildProperty方法中,可以看到mybatis框架是如何用数据库的属性映射到java对象的。下面这行代码,根据数据的属性名称,获取到了java对象属性名称。
String propertyName = reflector.findPropertyName(name);
reflector的findPropertyName方法简单直接,是从reflector类对象的一个全局map属性获取java属性名称。【在mybatis启动时,就已经给每个Mapper方法生成了reflector对象,并将返回对象的属性写到caseInsensitivePropertyMap中】
public String findPropertyName(String name) {
return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH));
}
在此,我将别名设置成了Per_son_Al_Signat_ure,在第二步中将下划线去除以后,这里的findPropertyName方法中,name参数为【PersonAlSignature】,方法中对此参数进行了二次加工,即转换为英文大写【PERSONALSIGNATURE】。如下图所示,最终根据此参数,获取到了java属性名称:【personalSignature】
5. 查询日志输出
可以看到最后成功进行了对象值映射: Per_son_Al_Signat_ure -> personalSignature
==> Preparing: select nickname ,avatar_url ,personal_signature as Per_son_Al_Signat_ure from tb_user where user_id = ?
==> Parameters: 83921(Integer)
<== Columns: nickname, avatar_url, Per_son_Al_Signat_ure
<== Row: yuzjang, null, 江海寄余生
18:15:21.527 logback [HikariPool-1 housekeeper] WARN com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=14m45s569ms312µs500ns).
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5242e9f7]
18:15:27.478 logback [main] INFO com.test.xxx.ForumTest - authorInfo:UserInfoResponse(personalSignature=江海寄余生, nickname=yuzjang, avatarUrl=null)