Mybatis中强大的功能元素:resultMap

转载自  Mybatis中强大的功能元素:resultMap

前言

在Mybatis中,有一个强大的功能元素resultMap。当我们希望将JDBC ResultSets中的数据,转化为合理的Java对象时,你就能感受到它的非凡之处。正如其官方所述的那样:

resultMap元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的长达数千行的代码。ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。

一、字段映射

在Mybatis中,最简单的结果映射方式,就是通过类型别名typeAliases来处理。

如果要这样做,那么第一步需要配置实体类包的路径:

mybatis.type-aliases-package=com.xxx.entity

该路径下的所有类,就会被注册到TYPE_ALIASES容器。我们在指定返回值类型的时候,就直接用别名即可。

比如,我们有一个User类:

@Data
public class User {private String id;private String username;private String password;private String address;private String email;
}

如果数据库中表的字段与User类的属性名称一致,我们就可以使用resultType来返回。

<select id="getUsers" resultType="User">SELECTu.id,u.username,u.password,u.address,u.emailFROMUSER u
</select>

当然,这是理想状态下,属性和字段名都完全一致的情况。但事实上,不一致的情况是有的,这时候我们的resultMap就要登场了。

如果User类保持不变,但SQL语句发生了变化,将id改成了uid。

<select id="getUsers" resultType="User">SELECTu.id as uid,u.username,u.password,u.address,u.emailFROMUSER u
</select>

那么,在结果集中,我们将会丢失id数据。这时候我们就可以定义一个resultMap,来映射不一样的字段。

<resultMap id="getUserByIdMap" type="User"><result property="id" column="uid"></result>
</resultMap>

然后,我们把上面的select语句中的resultType修改为resultMap="getUserByIdMap"。

这里面column对应的是数据库的列名或别名;property对应的是结果集的字段或属性。

这就是resultMap最简单,也最基础的用法:字段映射。

下面,我们看看其他几种标签都是怎么应用的。

元素名称 描述 constructor 用于在实例化类时,注入结果到构造方法中 association 关联一个对象 collection 关联多个对象 二、构造方法

如果你希望将结果注入构造方法里,就可以用到constructor元素。

比如,我们的User类增加了一个构造方法:

public User(String id, String name) {this.id = id+"--------";this.username = name+"--------";
}

我们需要在resultMap中定义constructor元素:

<resultMap id="getUserByIdMap" type="User"><constructor><idArg column="id" name="id" javaType="string"></idArg><arg column="username" name="name" javaType="string"></arg></constructor>
</resultMap>

其中,column代表数据库字段名称或者别名;name则是构造方法中的参数名称;javaType指定了参数的类型。

如你所想,这样指定构造方法后,我们结果集中的id和username属性都会发生变化。

{"id": "1001--------","username": "后羿--------","password": "123456","address": "北京市海淀区","email": "510273027@qq.com"
}

三、关联

在实际的业务中,我们的用户一般都会有一个角色。那么在User类里面一般也是以一个实体类来表示。

@Data
public class User {//省略用户属性...//角色信息private Role role;
}

我们在查询用户的时候,如果也希望看到它的角色信息,我们会这样来写查询语句:

<select id="getUserById" resultType="User">SELECTu.id,u.username,u.password,u.address,u.email,r.id as 'role_id',r.name as 'role_name'FROMUSER uLEFT JOIN user_roles ur ON u.id = ur.user_idLEFT JOIN role r ON r.id = ur.role_idwhere u.id=#{id}</select>

如上,就要查询单个用户以及用户的角色信息。不过在这里,我们不能用resultType=User来返回。

毕竟,User类中只有一个Role对象,并没有role_id和role_name字段属性。

所以,我们要使用association来关联它们。

<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><association property="role" javaType="Role"><id property="id" column="role_id"></id><result property="name" column="role_name"></result></association>
</resultMap>

最后我们就可以将角色信息一块显示出来:

{"id": "1001","username": "后羿","password": "123456","address": "北京市海淀区","email": "510273027@qq.com","role": {"id": "3","name": "射手"}
}

事实上,如果你确定关联信息是一对一的情况,有个更简便的方法可以替代association,我们在本文的第五部分-自动填充关联对象再看它是怎么实现的。

四、集合

1、集合的嵌套结果映射

上面我们看到一个用户后羿,它的角色是射手;但大部分时候,我们每个人都不可能只拥有一种角色。所以,我们需要将User类中的角色属性的类型改成List。

@Data
public class User {//省略用户属性...//角色信息private List<Role> roles;
}

现在就变成了一个用户对应多个角色,所以就不是简单的association。

因为association处理的是有一个类型的关联;而我们这里是有多个类型的关联,所以就需要用到collection属性。

我们整体的resultMap会变成下面这样:

<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="roles" ofType="Role"><id property="id" column="role_id"></id><result property="name" column="role_name"></result></collection>
</resultMap>

这样的话,即便你有多个角色也可以被正确显示:

{"id": "1003","username": "貂蝉","password": "123456","address": "北京市东城区","email": "510273027@qq.com","roles": [{"id": "1","name": "中单"},{"id": "2","name": "打野"}]
}

2、集合的嵌套 Select 查询

在大部分业务系统中,我们都会有一个菜单的表,比如像下面这样,一张Menu表:

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 这里我们给菜单分为两级。我们给前端返回菜单的时候,也是需要分级的,不可能将这7条数据平级展示。那么,在这里我们的Menu实体类如下:

@Data
public class Menu {private String id;private String name;private String url;private String parent_id;private List<Menu> childMenu;
}

一级菜单,包含一个二级菜单的列表,这里用childMenu来表示。

SQL语句中,如果没有parent_id字段属性,我们就先查所有的一级菜单:

<select id="getMenus" resultMap="menusMap">SELECTm.id,m.name,m.url,m.parent_idFROMm_menu mwhere 1=1<choose><when test="parent_id!=null">and m.parent_id = #{parent_id}</when><otherwise>and m.parent_id = '0'</otherwise></choose>
</select>

这个查询语句,在不传输任何参数的情况下,我们会得到两条一级菜单的数据。

那么在只调用此方法一次的情况下,怎么把所有的菜单信息查询出来,并按层级展示呢?

我们来看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>

重点来看collection元素:

property="childMenu" 对应的是菜单中的子级菜单列表;

ofType="Menu" 对应返回数据的类型;

select="getMenus" 指定了SELECT语句的id;

column="{parent_id=id}" 则是参数的表达式。

这个collection整体的含义可以这样理解:

通过getMenus这个SELECT语句来获取一级菜单中的childMenu属性结果;在上面的SELECT语句中,需要传递一个parent_id参数;这个参数的值就是一级菜单中的id。

通过这种方式,我们就可以得到已分级的所有菜单信息。

[{"id": "1","name": "系统管理","parent_id": "0","childMenu": [{"id": "1001","name": "用户管理","url": "/user","parent_id": "1"},{"id": "1002","name": "角色管理","url": "/role","parent_id": "1"},{"id": "1003","name": "单位管理","url": "/employer","parent_id": "1"}]},{"id": "2","name": "平台监控","parent_id": "0","childMenu": [{"id": "2001","name": "系统监控","url": "/system/monitor","parent_id": "2"},{"id": "2002","name": "数据监控","url": "/data/monitor","parent_id": "2"}]}
]

五、自动填充关联对象

我们知道,在Mybatis解析返回值的时候。

第一步是获取返回值类型,拿到Class对象,然后获取构造器,设置可访问并返回实例,然后又把它包装成MetaObject对象。

从数据库rs中拿到结果之后,会调用MetaObject.setValue(String name, Object value) 来填充对象。

在这过程中,有趣的是,它会以.来分隔这个name属性。

如果name属性中包含.符号,就找到.符号之前的属性名称,把它当做一个实体对象来处理。

可能笔者在这里描述的不够直观,我们还是来看例子。

在本文第三部分中,我们有一个用户对应一个角色的例子。

其中,User类定义如下:

@Data
public class User {//省略用户属性...//角色信息private Role role;
}

在这里,我们无需定义resultMap,直接返回resultType=User即可。不过需要把role信息的别名修改一下,重点是.符号

<select id="getUserList" resultType="User">SELECTu.id,u.username,u.password,u.address,u.email,r.id as 'role.id',r.name as 'role.name'FROMUSER uLEFT JOIN user_roles ur ON u.id = ur.user_idLEFT JOIN role r ON r.id = ur.role_id
</select>

这样,在Mybatis解析到role.id属性的时候,以.符号分隔之后发现,role别名对应的是Role对象,则会先初始化Role对象,并将值赋予id属性。

相关代码如图:

Mybatis中强大的功能元素:resultMap

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323133.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将编号为0和1的两个栈存放于一个数组空间V[m]中。

目录 1.题目描述 2.算法实现 1.题目描述 将编号为0和1的两个栈存放于一个数组空间V[m]中,栈底分别处于数组的两端。当第0号栈的栈顶指针top[0]等于-1时该栈为空&#xff1b;当第1号栈的栈顶指针top[1]等于m时该栈为空。两个栈均从两端向中间增长&#xff08;见图&#xff09;…

【JDBC】Eclipse连接Mysql

学习完servlet终于可以学习JDBC了&#xff0c;下载安装完mysql&#xff0c;导入jar包后&#xff0c;就可以利用java对mysql进行一系列的操作了 连接数据库并插入数据的代码 package com.k1998;import java.sql.Connection; import java.sql.Driver; import java.sql.DriverMa…

ASP.NET Core 2.0 依赖注入

问题 如何使用 ASP.NET Core 服务容器进行依赖注入&#xff1f; 答案 创建一个服务 public interface IGreetingService { string Greet(string to); } public class GreetingService : IGreetingService { public string Greet(string to){ return $"H…

CF741D-Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths【树上启发式合并】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidCF741D 题目大意 一棵根为111的树&#xff0c;每条边上有一个字符(a−v(a-v(a−v共222222种)))。 一条简单路径被称为Dokhtar−koshDokhtar-koshDokhtar−kosh当且仅当路径上的字符经过重新排序后可以…

用数组存储字符C进行回文检测

#include<bits/stdc.h> using namespace std;int main() {char c[20];int i,j;cout<<"请输入字符c:";cin>>c;int nstrlen(c);for(i0,jn-1;i<j;i,j--){if(c[i]!c[j]) break;}if(i>j) cout<<"是回文&#xff01;";else cout&l…

面试 - 要不简单聊一下你对MySQL索引的理解?

转载自 面试 &#xff0d; 要不简单聊一下你对MySQL索引的理解&#xff1f; MySQL索引&#xff1f;这玩意儿还能简单聊&#xff1f;明显是在挖坑&#xff0c;幸好老夫早有准备&#xff0c;切听我一一道来。 一、索引是什么? 索引是帮助MySQL高效获取数据的数据结构。 二、…

ABP从入门到精通(4):使用基于JWT标准的Token访问WebApi

项目&#xff1a;asp.net zero 4.2.0 .net core&#xff08;1.1&#xff09; 版本 我们做项目的时候可能会遇到需要提供api给app调用&#xff0c;ABP动态生成的WebApi提供了方便的基于JWT标准的Token访问方式供我们访问API&#xff0c;不用在代码上做任何改动&#xff0c;很方便…

jzoj2700-数字【数论,LCM】

正题 luogu题目链接:https://www.luogu.org/problemnew/show/P4193 题目大意 定义一个函数D(x)D(x)D(x)和S(x)S(x)S(x)&#xff0c;S(x)S(x)S(x)表示xxx的各位之和 D(n){D(S(n)),S≥10S(n)D(n)\left\{\begin{matrix} \\D(S(n)),S\geq 10 \\S(n) \\ \\ \end{matrix}\right.D(n…

【Servlet】获取并输出服务器获得的数据

获取&#xff1a; String name req.getParameter(“username”); System.out.println(name); 输出 PrintWriter pw resp.getWriter(); pw.write(name); 学习serlvet的最佳学习地点https://www.bilibili.com/video/av68412637/?p136servlet知识点&#xff1a;servlet接口&…

回文是指正读反读均相同的字符序列;如“abba”和“abdba”均是回文,但“good”不是回文。试写一个算法判定给定的字符序列是否是回文。(提示:将一半字符入栈)

文章目录 1.题目描述 2.算法实现 1.题目描述 回文是指正读反读均相同的字符序列&#xff1b;如“abba”和“abdba”均是回文&#xff0c;但“good”不是回文。试写一个算法判定给定的字符序列是否是回文。&#xff08;提示&#xff1a;将一半字符入栈&#xff09; 2.算法实现…

ASP.NET Core 2.0 + EF6 + Linux +MySql混搭

好消息&#xff01;特好消息&#xff01;同时使用ASP.NET Core 2.0和.NET Framework类库还能运行在linux上的方法来啦&#xff01; 是的&#xff0c;你没有看错&#xff01;ASP.NET Core 2.0&#xff0c;.NET Framework类库&#xff0c;linux通通都给你&#xff0c;不要998只要…

面试被问Mysql没答上来?阿里P5:总结了55道常见面试题,收藏一波

转载自 面试被问Mysql没答上来&#xff1f;阿里P5:总结了55道常见面试题&#xff0c;收藏一波 正文开始前&#xff0c;分享阿里 P8 高级架构师吐血总结的 《Java 核心知识体系&面试资料.pdf》, 非卖课程的哟&#xff01; 阿里 P8 级高级架构师吐血总结的一份 Java 核心知…

jzoj3511-cza的蛋糕【状态压缩dp,dfs】

正题 题目大意 n∗mn*mn∗m的矩阵&#xff0c;有些阻挡&#xff0c;求至少要多少个1∗21*21∗2或2∗12*12∗1的块铺在上面使得没用可以继续铺的地方。 解题思路 因为mmm很小&#xff0c;明显状压。 用fi,j,kf_{i,j,k}fi,j,k​表示第iii行&#xff0c;上一行状态为jjj&#xff…

【C单链表】链表与尾插法

struct struct的几种用法。 1、基本结构体定义 struct stu {int score;int id;char name[20]; };2、进阶结构体定义 struct stu {int score;int id;char name[20]; }st1;下面多了个st1&#xff0c;其实就相当于 struct stu {int score;int id;char name[20]; }; struct stu…

请设计一个栈,实现十进制数转任意进制数。

文章目录 1.题目描述 2.代码实现 1.题目描述 请设计一个栈&#xff0c;实现十进制数转任意进制数 2.代码实现 #include <iostream> using namespace std;#define MAXSIZE 100typedef struct {int *base;//栈底指针int *top;//栈顶指针int stacksize;//栈最大容量 } S…

开源纯C#工控网关+组态软件(四)上下位机通讯原理

一、 网关的功能&#xff1a;承上启下 最近有点忙&#xff0c;更新慢了。感谢园友们给予的支持&#xff0c;现在github上已经有。目标是最好的开源组态&#xff0c;看来又近一步^^ 之前有提到网关是物联网的关键环节&#xff0c;它的作用就是承上启下。 下位机有下位机的语言…

【Java】继承与多态

1、继承 由于一个对象功能的单一性&#xff0c;我们想对其进行扩展&#xff0c;但想保留其属性和功能&#xff0c;我们就必须用到继承。 java继承的特性&#xff1a; 单继承&#xff1a;java里的对象不同于C&#xff0c;只能进行单继承&#xff0c;即一个对象只能有一个父亲 …

C++描述 国王给骑士发放金币 ||

C描述 国王给骑士发放金币 || 国王将金币作为工资&#xff0c;发放给忠诚的骑士。第一天&#xff0c;骑士收到一枚金币&#xff1b;之后两天&#xff08;第二天和第三天&#xff09;&#xff0c;每天收到两枚金币&#xff1b;之后三天&#xff08;第四、五、六天&#xff09;&a…

jzoj3519-灵能矩阵【LCM,树形dp】

正题 题目大意 一棵树&#xff0c;每个叶子节点有权值&#xff0c;每个点的权值是它这棵子树中的所有叶子节点权值之和。可以减少叶子节点的值&#xff0c;要求减少最少的值使得对于每个点&#xff0c;它的所有子节点的权值都相等。 解题思路 如果将叶子节点的深度优先访问顺…

微软Tech Summit 2017,等你来打Call

2017年10月31至11月3日&#xff0c;由微软举办的Tech Summit 2017技术暨生态大会将在北京盛大举办&#xff0c;要在北京连开四天。今年的技术大会看头十足&#xff0c;不仅有大咖级人物带来十二大主题课程&#xff0c;更有三天四场的主题之夜。微软技术大会最早是由微软技术教育…