官方文档: mybatis – MyBatis 3 | XML 映射器https://mybatis.org/mybatis-3/zh/sqlmap-xml.html“resultMap 元素是 MyBatis 中最重要最强大的元素......ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了”
—— MyBatis 官方文档
当要查询的数据很复杂的时候,比如需要同时查询好几个表,比如要查询的 entity 中不仅有一些基础数据,还引用了某一个类,甚至还声明了某一个 List<> 集合。可以说要查询出来的数据还是很复杂的,这个时候就需要 resultMap 这个牛批的玩意了。
首先要了解,一个 resultMap 中都有些什么常用的玩意:
<resultMap id="唯一标识" type="映射的entity对象的绝对路径">
<id column="表主键字段" jdbcType="字段类型" property="映射entity对象的主键属性" />
<result column="表某个字段" jdbcType="字段类型" property="映射entity对象的某个属性"/>
<!-- 指的是entity对象中的对象属性 -->
<association property="entity中某个对象属性" javaType="这个对象的绝对路径">
<id column="这个对象属性对应的表的主键字段" jdbcType="字段类型" property="这个对象属性内的主键属性"/>
<result column="表某个字段" jdbcType="字段类型" property="这个对象属性内的某个属性"/>
</association>
<!-- 指的是entity对象中的集合属性 -->
<collection property="entity中的某个集合属性" ofType="这个集合泛型所存实体类的绝对路径">
<id column="这个集合属性中泛型所存实体类对象对应表的主键字段" jdbcType="字段类型"
property="这个集合属性中泛型所存实体类对象的主键属性"
/>
<result column="表某个字段" jdbcType="字段类型"
property="这个集合属性泛型所存实体类对象的属性"
/>
</collection>
<!-- 引用另一个resultMap (套娃) -->
<collection property="entity中的某个集合属性"
resultMap="这个引用的resultMap的type,就是这个集合属性泛型所存实体类的绝对路径"
/>
</resultMap>
注意:
一个<resultMap></resultMap>,就是对应着一个实体类。id就是他的名字,type就是他们的身体。实体类和对应表是一一对应的,实体类中每一个属性都对应着一个表中的字段。其中有个别属性上标明注解@TableField(exist = false),其含义就是该属性在表中不存在,大多为引用的属性,引用的集合,或者是为了满足某种业务需求而声明的某种标志(比如sql多表查询时把某个表中的某个字段数据给另一个实体中某个属性赋值而起的别名)等等。
那么对于实体类中的各个属性:
主键id则建议使用 <id column="" ../>
一般的字段建议使用 <result column="" ../>
引用了另外某个实体类对象 <association ... />
引用了由某个实体类组成的集合 <collection ... />
那么使用时还需要注意什么?
如果是单表映射的话,其中的 <result column=" " ../> 中的值,默认情况下就是对应着数据库的字段。这玩意可能底层是这么实现的,可能表中的下划线_和Java规范中的驼峰命名是有自动转换的功能的。完了它自己匹配。但是要注意这个column的值,对应的其实是指实际查询出来的字段。也就是说如果你给它起了个别名,那它可能就不知道它自己是谁了。
举个栗子:
正常情况下:column默认值为字段
<resultMap id="roleMap" type="com.wlong.modules.base.entity.RoleEntity"> <result property="roleName" column="role_name" /> </resultMap> <select id="selectRoleById" parameterType="Integer" resultMap="roleMap"> select * from `role` where id = #{id} </select> <!-- 结果为:Role{ id=1, roleName='wlong', age=20} -->
如果查询字段起了别名,那么column的值就是别名
<resultMap id="roleMap" type="com.wlong.modules.base.entity.RoleEntity"> <result property="roleName" column="r"></result> </resultMap> <select id="queryRoleById" parameterType="Integer" resultMap="roleMap"> select id, role_name r, age, from `role` where id = #{id} </select> <!-- 结果为:Role{ id=1, roleName='wlong', age=20} -->
反例:如果查询时使用别名,而column值没修改时(roleName结果为null)
<resultMap id="roleMap" type="com.wlong.modules.base.entity.RoleEntity"> <result property="roleName" column="role_name"></result> </resultMap> <select id="queryRoleById" parameterType="Integer" resultMap="roleMap"> select id, role_name r, age, from `role` where id = #{id} </select> <!-- 结果为:Role{ id=1, roleName='null', age=20} -->
所以当声明好了 <resultMap> 该如何使用?
<sql id="随便起个名" >
表别名2.字段名1 , 表别名2.字段名2 ,表别名2.字段名3 ,....... 表别名2.字段名n
</sql>
<!--查询表1的全部信息,同时把关联表2的信息也查出来(通过where决定要查出什么)-->
<select id="dao和service中定义的方法名" resultMap="要查的某个entity对象的resultMap的id">
select a.*,<include refid="上边随便起的那个名"/> from 表1名 表别名1, 表2名 表别名2
<where>
...
</where>
</select>
之所以搞出来一个<sql>,那纯纯是为了方便。执行一次数据库的查询,只需要拿到想要的个别信息就行了,没必要咔咔都给查出来,瞅着都烦气。而且要是需要的不需要的全查出来,浪费时间不说,还浪费内存。甚至浪费感情。= =
前端需要什么,后端就给传什么,数据库就给查什么。所有的数据处理要求sql基本都可以满足。要知道你每敲的一行代码全部都是围绕着业务去做的,撇开业务不谈的代码都是无用功。
推荐去看一个大佬的文章,本文中有些是借鉴大佬原文的,结合例子写的非常详细。感谢感谢!
还有这个大佬,写的也非常好。感谢感谢!
.The End