热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

Mybatis中强大的resultMap功能介绍

这篇文章主要给大家介绍了关于Mybatis中强大的resultMap功能的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Mybatis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

在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来返回。

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

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

那么,在结果集中,我们将会丢失id数据。这时候我们就可以定义一个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元素:


	
		
		
	

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

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

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

三、关联

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

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

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

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

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

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


	
	
	
	
	
	
	
		
		
	

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

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

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

四、集合

1、集合的嵌套结果映射

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

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

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

因为association处理的是有一个类型的关联;而我们这里是有多个类型的关联,所以就需要用到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 childMenu;
}

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

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

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

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

我们来看menusMap的定义:


	
	
	
	
	
	
	

重点来看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信息的别名修改一下,重点是.符号

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

相关代码如图:


总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。


推荐阅读
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了一个免费的asp.net控件,该控件具备数据显示、录入、更新、删除等功能。它比datagrid更易用、更实用,同时具备多种功能,例如属性设置、数据排序、字段类型格式化显示、密码字段支持、图像字段上传和生成缩略图等。此外,它还提供了数据验证、日期选择器、数字选择器等功能,以及防止注入攻击、非本页提交和自动分页技术等安全性和性能优化功能。最后,该控件还支持字段值合计和数据导出功能。总之,该控件功能强大且免费,适用于asp.net开发。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文介绍了通过mysql命令查看mysql的安装路径的方法,提供了相应的sql语句,并希望对读者有参考价值。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
author-avatar
阳阳的微薄之歌
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有