参考博文:juejin.cn/post/684490… 作者:清幽之地
resultMap是用来干嘛的?
- 将JDBC ResultsSets中的数据转化成为Java对象
- 将复杂的语句编写为映射代码
映射
- 通过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的具体用法
- 上面这个例子是说了resultMap到底是干嘛用的,接下来继续说明具体的一些用法
- resultMap中的几个字标签
- constructor:构造器法,很不建议用这个方法,要求和构造函数中的顺序一一对应,比较麻烦
- association:一对一用这个
- collection:一对多用这个
- 接下来举一个一对一的例子
@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字段命上不同的名字。
- 接下来是一对多的例子,一对多非常非常重要,这里修改一下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>
- 其实还有另一种写法,就是多个resultMap的嵌套,可以自行去了解
- 这里补充另一点,就是为什么要在实体类上加上@Data,因为前面提到了一种不建议使用的resultMap方式,就是构造器注入,那不用构造器的话是用的什么将各个属性进行注入的呢?@Data提供了各个属性的get/set方法,也可以实现属性的注入。
菜单权限案例引入
| id | name | url | parent_id |
|---|---|---|---|
| 1 | 系统管理 | 0 | |
| 1001 | 用户管理 | /user | 1 |
| 1002 | 角色管理 | /role | 1 |
| 1003 | 单位管理 | /employer | 1 |
| 2 | 平台监控 | 0 | |
| 2001 | 系统监控 | /system/monitor | 2 |
| 2002 | 数据监控 | /data/monitor | 2 |
- 现在很多业务系统为了统一管理,基本上菜单都不是写死在前端中的,都是在数据库中建一张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>