mybatis的resultMap

138 阅读4分钟

参考博文:juejin.cn/post/684490… 作者:清幽之地

resultMap是用来干嘛的?

  1. 将JDBC ResultsSets中的数据转化成为Java对象
  2. 将复杂的语句编写为映射代码

映射

  1. 通过typeAliases进行别名映射(可以这样理解,将sql查询出的结果包装秤一个类对象返回)
  • 首先配置需要别名的实体类路径:mybatis.type-aliases-package=com.xxx.entity
  • 这时一个类User
@Data
public class User {
    private String id;
    private String username;
    private String password;
    private String address;
    private String email;
}
<select id="getUsers" resultType="User">
	SELECT
		u.id,
		u.username,
		u.password,
		u.address,
		u.email
	FROM
		user u
</select>

既然这样已经可以实现将结果映射成为一个类对象,为什么还需要resultMap呢??? 其实很好解释这个问题,你查出来的结果一定存在这么一个对应的类吗,如果从多张表中查询就不得不考虑组装的问题了

  • 我们在上面的例子上进行修改
<select id="getUsers" resultType="User">
	SELECT
		u.id as uid,
		u.username,
		u.password,
		u.address,
		u.email
	FROM
		user u
</select>

这个时候u.id被起了一个别名uid,拿着uid去User类去找匹配项根本找不到的,所以这边的resultType="User"实际上就不适用了,必须另寻他法

<resultMap id="getUserByIdMap" type="User">
    <result property="id" column="uid"></result>
</resultMap>
  • 把上面的resultType改成getUserByIdMap,也就是resultMap的id
  • result标签中的column对应的是数据库的列名或者别名,property是对应的结果集(类中字段)名称

resultMap的具体用法

  1. 上面这个例子是说了resultMap到底是干嘛用的,接下来继续说明具体的一些用法
  2. resultMap中的几个字标签
  • constructor:构造器法,很不建议用这个方法,要求和构造函数中的顺序一一对应,比较麻烦
  • association:一对一用这个
  • collection:一对多用这个
  1. 接下来举一个一对一的例子
@Data
public class User {
    //内嵌一个角色属性,一个用户对应一个角色,比如玩狼人杀,不可能又是村民,又是猎人
    private Role role;
}
  • 下面是不使用resultMap的写法
<select id="getUserById" resultType="User">
        SELECT
            u.id,
            u.username,
            u.password,
            u.address,
            u.email,
            r.id as 'role_id',
            r.name as 'role_name'
        FROM
            user u
                LEFT JOIN user_roles ur ON u.id = ur.user_id
                LEFT JOIN role r ON r.id = ur.role_id
        where u.id=#{id}
    </select>                                 

但是这边存在一个问题,就是User对象中只有Role对象并没有role_id和role_name属性,实际上是不行的

  • 正确的实现方法如下:最后把resultMap作为resultType的值传过去既可以实现一对一关系
<resultMap id="userMap" type="User">
        <!--下面五个是User中的字段-->
	<id property="id" column="id"></id>
	<result property="username" column="username"></result>
	<result property="password" column="password"></result>
	<result property="address" column="address"></result>
	<result property="email" column="email"></result>
	<!--association中放的是一对一关系的内嵌类Role-->
	<association property="role" javaType="Role">
		<id property="id" column="role_id"></id>
		<result property="name" column="role_name"></result>
	</association>
</resultMap>

这里补充一个很重要的地方,就是User中的id字段和Role中的id字段的column不要用同一个名字。一对一可能还不会出问题,一对多的时候很容易只能查出来一个结果,可以通过在sql语句中起别名解决,或者在建表的时候就把主键id字段命上不同的名字。

  1. 接下来是一对多的例子,一对多非常非常重要,这里修改一下User类
@Data
public class User{
    //一个人可以有多部手机
    private List<Phone> phones;
}
<resultMap id="userMap" type="User">
	<id property="id" column="id"></id>
	<result property="username" column="username"></result>
	<result property="password" column="password"></result>
	<result property="address" column="address"></result>
	<result property="email" column="email"></result>
	
	<collection property="phones" ofType="Phone">
		<id property="id" column="phone_id"></id>
		<result property="name" column="phone_name"></result>
	</collection>
</resultMap>
  1. 其实还有另一种写法,就是多个resultMap的嵌套,可以自行去了解
  2. 这里补充另一点,就是为什么要在实体类上加上@Data,因为前面提到了一种不建议使用的resultMap方式,就是构造器注入,那不用构造器的话是用的什么将各个属性进行注入的呢?@Data提供了各个属性的get/set方法,也可以实现属性的注入。

菜单权限案例引入

idnameurlparent_id
1系统管理0
1001用户管理/user1
1002角色管理/role1
1003单位管理/employer1
2平台监控0
2001系统监控/system/monitor2
2002数据监控/data/monitor2
  • 现在很多业务系统为了统一管理,基本上菜单都不是写死在前端中的,都是在数据库中建一张menu表进行菜单的统一管理
  • 菜单实体类如下
@Data
public class Menu {
    private String id;
    private String name;
    private String url;
    private String parent_id;
    private List<Menu> childMenu;
}
  • 先查一级菜单,一级菜单没有父菜单,也就是parent_id='0'
<select id="getMenus" resultMap="menusMap">
	SELECT
		m.id,
		m.name,
		m.url,
		m.parent_id
	FROM
		m_menu m
	where 1=1
	<choose>
		<when test="parent_id!=null">
			and m.parent_id = #{parent_id}
		</when>
		<otherwise>
			and m.parent_id = '0'
		</otherwise>
	</choose>
</select>

解释一下这个select语句,当parent_id不为空的时候,就说明该菜单不是一级菜单,否则该菜单的父菜单就是0

  • menusMap的定义
<resultMap id="menusMap" type="Menu">
	<id property="id" column="id"></id>
	<result property="name" column="name"></result>
	<result property="url" column="url"></result>
	<result property="m_desc" column="m_desc"></result>
	<result property="parent_id" column="parent_id"></result>
	
	<collection property="childMenu" ofType="Menu" select="getMenus"  column="{parent_id=id}"></collection>
</resultMap>