resultMap的作用
<resultMap>标签
用于封装sql的查询结果,可以包装成一个简单POJO对象,也可以包装成我们自定义的对象,只要我们使用<result>子标签
指定好查询结果的列和对象的属性之间的对应关系就好了。
官方原因
<id property="id" column="id"/>
是用于提高性能,但是在一些情况下,没有Id的话结果会出错。
我们对 id标签的理解是,它配置的字段为表的主键(联合主键时可以配置多个 id 标签),因为myBatis 中resultMap 只用于配置结果如何映射,并不知道这个表具体如何。 id 的唯一作用就是在嵌套的映射配置中判断数据是否相同,当配置id标签时, Mybatis只需要逐条比较所有数据中 id 标签配置的字段值是否相同即可。在配置嵌套结果查询时,配置 id 标签可以提高处理效率。 这样一来,如果有两条数据 userMap 部分的id相同 所以它们属于同一个用户,因此这条数据会合并到同一个用户中。id标签的设置还是很有必要的,如果仅由resultMap标签完成,当存在多条数据结果是,查询时间会十分的长,这时id标签设置就显得十分的有必要。
例子1
id是resultMap以及Collection的子标签,标记出作为 ID 的结果可以帮助提高整体性能。特别注意的是,id是当前命名空间中的一个唯一标识,用于标识一个结果映射。
如下图,itemId(商品id)字段值在数据库中不唯一,错误使用会导致只返回该订单某商品的一条记录。因为对于某个商品,麻辣味和五香味只是商品规格,其商品id是相同的。
例子2
MyBatis的resultMap只用于配置结果如何映射,id的唯一作用就是在嵌套的映射配置时判断数据是否相同,当配置id标签时,MyBatis只需要逐条比较所有数据中id标签字段值是否相同即可,可以提高处理效率。
为了更清楚地理解id 的作用,可以临时对userMap 的映射进行如下修改。
<resultMap id="userMap" type="test.mybatis.simple.model.SysUser"> <id property="userPassword" column="user_password"/> <result property="userName" column="user_name"></result> <result property="id" column="id"></result> <result property="userEmail" column="user_email"></result> <result property="userInfo" column="user_info"></result> <result property="headImg" column="head_img" jdbcType="BLOB"></result> <result property="createTime" column="create_time" jdbcType="TIMESTAMP"></result> </resultMap> <resultMap id="userRoleListMap" extends="userMap" type="SysUser"> <collection property="roleList" columnPrefix="role_" resultMap="test.mybatis.simple.mapper.RoleMapper.roleMap"/> </resultMap>
在测试数据中,用户的密码都是123456 ,因此如果用密码作为id,所有结果将合并为1条数据。
用户数: 1 用户名:admin 角色名:管理员 角色名:普通用户
对比之前的正确结果,可以看到角色记录也合并了
用户数:2 用户名:admin 角色名称:管理员 角色名称:普通用户 用户名:test 角色名称:普通用户
- id标签也可以配置多个,比如联合主键时
- 很可能也会出现没有配置id的情况,这时MyBatis就会把resultMap中所有字段进行比较,如果所有字段的值都相同就合并,只要有一个字段值不同,就不合并。这时,当结果集字段数为M,记录数N,最少M×N次比较,相比配置id时的N次比较,效率相差更多,所以要尽可能配置id标签。
- 在嵌套结果配置id属性时如果查询语句中没有查询id属性配置的列,就会导致id对应的值为null。这种情况下,所有值的id都相同,因此会使嵌套的集合中只有一条数据。所以在配置id列时,查询语句中必须包含该列。
例子3
给出这样一个场景,sql查询每个国家下的用户数,第一列是用户数,后面三列是一个国家对象所含的信息。
现在我要把结果集封装成如下自定义的对象集合。
其中的Country是一个简单类,有三个属性,和上图查询结果的后三列同名。
public class MyCountResult { private Long count; private Country country; }
在mapper文件中自定义resultMap(红色是因为省略了很长的限定名)
这样确实能把上面sql查询图的结果封装成 MyCountResult 类
- 列count 对应 类的count属性
- 剩下的列 对应 类的coutry嵌套类 (这里的列也复用了别的resultMap)
但是 返回的MyCountResult 对象却只有3个,而不是上面查询结果图中的十几个。
调试发现,count为0的对象只有1个!
这里是为什么呢?
原因:
<collection>标签,用于包装成集合,默认把id相同的封装在同一个集合内
在这里,由于没有指定 < id > 标签,导致mybatis把我的count列当成了id,而很不幸,count列有很多重复的0,所以把所有count为0的行封装成了一个集合,注入到MyCountResult类中,但是这个类中的country属性不是一个数组,所以mybatis只能把刚才的集合中的其中一个注入成country属性。
结语与反思:
既然官方要我们加入 id 标签,无论是性能还是正确性,我们都应该加入此标签。
而且作为id的列中的值必须是唯一的,也就是可以唯一标识这一行,例如刚才我的count列就有很多相同的0,也就失去了id这个词的意义。