源自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会报错……
但新写法不会,甚至还会有代码提示,写法2也不会
所以,就引出了为啥我会探究这个问题……我把它写成了这样:
然后就报错了……红红火火的报错:
There is no getter for property named 'userInfo' in 'class com.momincong.teamsystem.model.dto.UserDto'
折腾源码
定位
这里因为要还原问题,用的是方法2进行的debug
先从PreparedStatementHandler
的query
开始,进入handleResultSets
,这用来输出多个ResultSet的,对我们现在无用
所以我们直接走到handleResultSet(rsw, resultMap, multipleResults, null)
,输出单个ResultSet的方法,再进入到绑定属性的关键方法handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null)
(别问我resultHandler干嘛的,我暂时也不知道)
先是通过resultMap.hasNestedResultMaps()
判断传入的resultMap有没有子resultMap,这个Boolean值是在resultMap的build中设置的,我们这有个assoiciation,所以就是有了,这时我们进入handleRowValuesForNestedResultMap
这个方法
我们在这里进入getRowValue,这里是真正将resultMap解析成Object的地方
红色框出的部分是当前的object被根据resultMap中的type创建出来,随后就被 configuration.newMetaObject(rowValue)
包装起来(红框下两行)
随后,所有能直接get到的元素都会在applyPropertyMappings
中被写入metaObject中
这里经过了两层判断,1是判断该元素是不是子resultMap,2是判断该元素是否与结果有对应关系,随后在调试部位metaObject.setValue(property, value)
被写入
这里画个重点metaObject.setValue,这也是为什么会支持写法1的关键点
如果是子resultMap的元素,column会被留null,然后被跳过
全部元素遍历完后,这个方法就结束了,我们回到getRowValue,进入applyNestedResultMappings方法中
随后继续遍历resultMap,找到子resultMap
随后,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,自然就找不到,就报错了