源自IDEA的一次意外提示,卡了我一天

PS: 为啥我搜遍全网的博客,都看不到这样方便的写法?清一色的association

PSPS:在这补一下我研究学习的参考资料:
B站视频:MyBatis源码解析大合集
一篇写的很好的CSDN博客:mybatis MetaObject 浅析

先放上写法,以下简称写法1

<resultMap id="teamUserDto" type="TeamUserDto">
    <result column="team_name" property="teamName"/>
    <result column="work_id" property="workId"/>
    <result column="role" property="role"/>
    <result column="name" property="userInfo.name"/>
    <result column="work_id" property="userInfo.workId"/>
    <result column="logo" property="userInfo.logo"/>
    <result column="sex" property="userInfo.sex"/>
    <result column="phone" property="userInfo.phone"/>
    <result column="mail" property="userInfo.mail"/>
</resultMap>

与旧写法对比一下

<resultMap id="teamUserDto" type="TeamUserDto">
    <result column="team_name" property="teamName"/>
    <result column="work_id" property="workId"/>
    <result column="role" property="role" />
    <association property="userInfo" javaType="UserDto">
        <result column="name" property="name"/>
        <result column="work_id" property="workId"/>
        <result column="logo" property="logo"/>
        <result column="sex" property="sex"/>
        <result column="phone" property="phone"/>
        <result column="mail" property="mail"/>
    </association>
</resultMap>

其实有一种写法也蛮香的,我觉得比我这种特殊写法还香一点,以下简称写法2

<resultMap id="TeamUserDtoResMap" type="TeamUserDto">
    <result column="team_name" property="teamName"/>
    <result column="work_id" property="workId"/>
    <result column="role" property="role"/>
    <association property="userInfo" resultMap="UserInfoResMap"/>
</resultMap>

<resultMap id="UserInfoResMap" type="UserDto">
    <result column="name" property="name"/>
    <result column="work_id" property="workId"/>
    <result column="logo" property="logo"/>
    <result column="sex" property="sex"/>
    <result column="phone" property="phone"/>
    <result column="mail" property="mail"/>
</resultMap>

亿点点小吐槽

有没有感觉有点流弊……其实也没有……就是卡了大半天有点恼火

主要是旧写法IDEA 2021.1.1会报错……
image.png

但新写法不会,甚至还会有代码提示,写法2也不会
image.png

所以,就引出了为啥我会探究这个问题……我把它写成了这样:
image.png

然后就报错了……红红火火的报错:

There is no getter for property named 'userInfo' in 'class com.momincong.teamsystem.model.dto.UserDto'

折腾源码

定位

这里因为要还原问题,用的是方法2进行的debug

先从PreparedStatementHandlerquery开始,进入handleResultSets,这用来输出多个ResultSet的,对我们现在无用

所以我们直接走到handleResultSet(rsw, resultMap, multipleResults, null),输出单个ResultSet的方法,再进入到绑定属性的关键方法handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null)(别问我resultHandler干嘛的,我暂时也不知道)

image.png

先是通过resultMap.hasNestedResultMaps()判断传入的resultMap有没有子resultMap,这个Boolean值是在resultMap的build中设置的,我们这有个assoiciation,所以就是有了,这时我们进入handleRowValuesForNestedResultMap这个方法

image.png

我们在这里进入getRowValue,这里是真正将resultMap解析成Object的地方
image.png
红色框出的部分是当前的object被根据resultMap中的type创建出来,随后就被 configuration.newMetaObject(rowValue)包装起来(红框下两行)

image.png
随后,所有能直接get到的元素都会在applyPropertyMappings中被写入metaObject中

image.png

这里经过了两层判断,1是判断该元素是不是子resultMap,2是判断该元素是否与结果有对应关系,随后在调试部位metaObject.setValue(property, value)被写入

这里画个重点metaObject.setValue,这也是为什么会支持写法1的关键点

如果是子resultMap的元素,column会被留null,然后被跳过
image.png

全部元素遍历完后,这个方法就结束了,我们回到getRowValue,进入applyNestedResultMappings方法中

image.png

随后继续遍历resultMap,找到子resultMap
image.png

image.png
随后,1获取子resultMap的非空列,2和父元素的非空列组合在一起,3查看缓存map是否有对应的完成的元素,4递归调用getRowValue,只不过这次就是只传入子resultMap了,剩下的就是一般的合并子resultMap和父resultMap的Object

到此基本的逻辑讲完了,接下来进入下一个重点

MetaObject

这是一个mybatis内置极为强大的底层类,用于对象的创建与赋值,同时因为其几乎不使用外部依赖,可以直接借鉴到自己的代码里23333

这里摘录自https://blog.csdn.net/mz4138/article/details/81671319的例子

@Test
public void testTokenizer(){
    Animal animal = new Animal();
    animal.setParent(new Animal());
    MetaObject metaObject = MetaObject.forObject(animal, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
    metaObject.setValue("parent.name","tokenizer");
    System.out.println(animal.getParent().getName());
}

可以很明显的看到metaObject.setValue("parent.name","tokenizer");这行代码,metaObject是支持直接“点”子属性的,这也是为什么写法1能生效的原因

小结

至此,看到metaObject的用法,也应该知道为什么我当时会报There is no getter for property named 'userInfo' in 'class com.momincong.teamsystem.model.dto.UserDto'这个错了吧,因为在association中点出来,就表示的是子元素的子元素,会从userInfo中找userInfo,自然就找不到,就报错了