原贴地址:https://www.cnblogs.com/nick-guo-sdly/p/7668462.html

最近开发中遇到了很多树形结构数据的需要,利用mybatis提供嵌套查询功能,基本上可以完美解决,但是对于其中的原理并不理解,导致在使用的时候像瞎猫碰死耗子一样,照着先前成功的例子copy,后来遇到了莫名奇怪的报错迟迟不能解决,于是百度了一番,大致了解了背后的原理,整理如下。

  以简单的角色-菜单为例

 表结构

其中menu为菜单表,role为角色表,roleandmenu是中间表,角色和菜单为多对多的关系,现在我们需要下图所示的实体类

 1 import java.util.List;2 3 public class RoleInfo {4     5     private Integer roleid;6     private String rolename;7     private List<Menu> menulist;8     public Integer getRoleid() {9         return roleid;
10     }
11     public void setRoleid(Integer roleid) {
12         this.roleid = roleid;
13     }
14     public String getRolename() {
15         return rolename;
16     }
17     public void setRolename(String rolename) {
18         this.rolename = rolename;
19     }
20     public List<Menu> getMenulist() {
21         return menulist;
22     }
23     public void setMenulist(List<Menu> menulist) {
24         this.menulist = menulist;
25     }
26
27     @Override
28     public String toString() {
29         return "RoleInfo [roleid=" + roleid + ", rolename=" + rolename + ", menulist=" + menulist + "]";
30     }
31
32 }

 第一种方法:利用嵌套语句查询

 1 <resultMap type="com.test.mybatis.model.RoleInfo" id="roleModel">2         <id column="id" property="roleid"/>3         <result column="name" property="rolename"/>4         <collection property="menulist" select="getMenu" column="id">5             6         </collection>7     </resultMap>8     9     <select id="getRoleInfo" resultMap="roleModel">
10         select id,name from role
11     </select>
12
13     <select id="getMenu" resultType="com.test.mybatis.model.Menu">
14         select m.id,m.name
15         from menu m join roleandmenu ram on m.id=ram.menuId
16         where ram.roleId=#{id}
17     </select>

 1     @Test2     public void testRoleAndMenu() throws IOException {4         Reader reader = Resources.getResourceAsReader("mybatis.xml");5         SqlSessionFactory sqlsessionfac = new SqlSessionFactoryBuilder().build(reader);6         SqlSession sqlsession = sqlsessionfac.openSession();7         try {8             RoleAndMenuMapper mapper = sqlsession.getMapper(RoleAndMenuMapper.class);9             System.out.println(JSONObject.toJSON(mapper.getRoleInfo()));
10         } catch (Exception e) {
11             // TODO: handle exception
12             e.printStackTrace();
13         } finally {
14             sqlsession.close();
15         }
17     }

结果:

原理如下:

  1.mybatis先执行getRoleInfo这个查询,获取结果集

  2.从ResultSet中逐一取出记录,构建RoleInfo对象并为映射属性赋值

  3.赋值过程中发现目标menulist属性配置了一个关联集合(collection),此时执行id为collection标签中select属性值(getMenu)的查询,并将当前记录中的id属性作为此查询的参数。(association标签同理)

  4.将关联查询返回的结果映射到meunlist属性

  5.执行步骤2,直至ResultSet.next=false

  6.返回查询结果

  这种方式的好处在于简单易懂,通过简单的配置就可以达到目标效果。不足之处在于如果结果集记录条数过大,会造成较大的数据库访问消耗,因为在从ResultSet中取出记录的时候每取一条,便执行一次关联查询,假设一次查询的结果集有10条记录,则数据库的访问次数为:关联查询次数(10)+返回结果集的查询(1)=11次。

需要注意的地方

 1.collection/association标签的column属性:当向关联查询传递的参数个数为1时,column的值应为结果集中的列名,而不是映射属性名(property),上面的例子中,向关联查询传递id值,column的值应为id而不是roleid。

  可以向关联查询传递多个参数,此时column的值为多个键值对,如下图

  此时向关联查询传递了两个参数id和name,此时还应该将关联的查询的parameterType改为java.util.Map,否则关联查询无法接受参数

  2.在进行单一类型树形结构查询的时候,需要注意关联查询的结果集中的列是否有作为查询条件的列

  这样说可能比较别扭,以上面的menu表为例,有一个parent列用于存储父部门的ID,使用嵌套查询获取以下实体类

 1 import java.util.List;2 3 public class MenuTree {4     5     private Integer id;6     private String name;7     private List<Menu> children;8     public Integer getId() {9         return id;
10     }
11     public void setId(Integer id) {
12         this.id = id;
13     }
14     public String getName() {
15         return name;
16     }
17     public void setName(String name) {
18         this.name = name;
19     }
20     public List<Menu> getChildren() {
21         return children;
22     }
23     public void setChildren(List<Menu> children) {
24         this.children = children;
25     }
26
27
28 }

  此时会产生一个问题:每个顶级菜单(parent为空的菜单)的子菜单只有一个查询结果,这是因为在关联查询getSubMenu中没有将id查询出来,而关联查询和主查询的resultMap一样,所以关联查询在映射结果集的时候就会再次去执行关联查询,而由于本次关联查询并没有取出id这个作为参数的属性,所以实际上只执行了N(结果集记录数)次关联查询。

  因此,在这种情况中,必须在关联查询中查询出id这个列,否则会查询不出预期结果。

 第二种方法:使用嵌套结果集

 1 <resultMap type="com.test.mybatis.model.RoleInfo" id="roleModel">2         <id column="id" property="roleid"/>3         <result column="name" property="rolename"/>4         <collection property="menulist" ofType="com.test.mybatis.model.Menu">5             <id column="menuid" property="id"/>6             <result column="menuname" property="name"/>7             <result column="description" property="description"/>8             <result column="parent" property="parent"/>9             <result column="createdate" property="createdate"/>
10             <result column="modifydate" property="modifydate"/>
11         </collection>
12     </resultMap>
13
14     <select id="getRoleInfo" resultMap="roleModel">
15         select
16             ram.roleid as id,
17             ro.name as name,
18             me.id as menuid,
19             me.name as menuname,
20             me.description,
21             me.parent,
22             me.createdate,
23             me.modifydate
24         from roleandmenu ram
25             left outer join role ro on ram.roleid=ro.id
26             left outer join menu me on ram.menuid=me.id
27     </select>

  原理是通过关联查询,一次性将数据查询出来,然后根据resultMap的配置进行转换,构建目标实体类。

  显然,这种方法更为直接,只需要访问一次数据库就可以了,不会造成严重的数据库访问消耗。

  此外,我还发现了一个无解的情况,如果把嵌套结果集的返回值类型全部改成HashMap的话,会导致menulist里只有一行数据

  

  解决的办法是,给collection标签的javaType赋值为目标集合类型

  

  找了很久,终于在官方文档里找到了解释

  

mybatis查询树形数据的两种方法相关推荐

  1. 获得GPS数据的两种方法 1读串口

    获得GPS数据的两种方法 1读串口 - [技术] 版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明 http://memset.blogbus.com/logs/17801310.ht ...

  2. java构造和解析json_Java构造和解析Json数据的两种方法详解一

    在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面首先介绍用json-lib构造和解析Jso ...

  3. java json解析 代码_Java构造和解析Json数据的两种方法详解一

    在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面首先介绍用json-lib构造和解析Jso ...

  4. Java构造和解析Json数据的两种方法详解一

    在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面首先介绍用json-lib构造和解析Jso ...

  5. java在文件的后面添加_java 在file的尾部添加数据的两种方法总结

    java 在file的尾部添加数据的两种方法总结 问题描述: 在文件的末尾追加内容 方法1:利用RandomAccessFile类 1.将randomAccessFile模式设置为rw 2将rando ...

  6. java构建json_Java构造和解析Json数据的两种方法详解一

    在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面首先介绍用json-lib构造和解析Jso ...

  7. SQLServer 批量插入数据的两种方法

    SQLServer 批量插入数据的两种方法- 发布:dxy 字体:[增加 减小] 类型:转载 在SQL Server 中插入一条数据使用Insert语句,但是如果想要批量插入一堆数据的话,循环使用In ...

  8. java 文件尾部_java 在file的尾部添加数据的两种方法总结

    java 在file的尾部添加数据的两种方法总结 问题描述: 在文件的末尾追加内容 方法1:利用RandomAccessFile类 1.将randomAccessFile模式设置为rw 2将rando ...

  9. mvc控制器接收数据的两种方法

    一.mvc控制器接收数据的两种方法 A方法: public ActionResult ProcessAdd() { string username=Request["UserName&quo ...

  10. 通达信交易接口api_股票量化交易-获取数据的两种方法

    量化交易第一步就是获取数据,介绍两种免费的方法 1.通过pytdx获取本地通达信数据 2.通过requests爬虫爬取腾讯财经数据 通过python第三方库pytdx获取 这是个很强大的第三方库,原理 ...

最新文章

  1. 跨平台PHP调试器设计及使用方法——使用
  2. python列出文件夹最新的几个文件_Python列出一个文件夹及其子目录的所有文件
  3. 爬虫学习笔记(三)—— requests库
  4. Angular中实现动态路由跳转并传递参数
  5. JPG各种输入框样式
  6. Mozilla考虑支持H.264
  7. java中单例模式用法详解
  8. 结对项目开发之电梯调度问题
  9. php try 错误_PHP异常和错误(2)异常的基本处理:try
  10. 和我一起学《HTTP权威指南》——Web服务器
  11. python 对字符串逆序输出
  12. 惊喜来袭~进阶版《看漫画学Python 2:有趣、有料、好玩、好用》
  13. mkv无损转换成mp4,方法步骤
  14. linux dd安装win2003,DD安装win2003,2008系统
  15. Flutter之国际化语言
  16. 怎么把备忘录中的视频导到手机相册里
  17. 邮件营销活动如何被病毒式传播?
  18. 2018麦考林杂志计算机科学,2018年加拿大大学麦考林杂志排名发布,快来围观你喜欢的学校排名有什么变动没?...
  19. 扩充计算机内存是扩充什么,怎样扩大电脑内存 电脑扩大内存方法【图文】
  20. libvirt介绍和使用

热门文章

  1. oracle查询语句转sql,将sql server查询语句转换为oracle查询语句[紧急]
  2. 自动驾驶 10-2: 惯性测量单元 (IMU)The Inertial Measurement Unit (IMU)
  3. 极客大学架构师训练营 微服务架构 Service Mesh 服务网格 RPC 协议实现原理 Dubbo 通讯协议 第19课 听课总结
  4. 易筋SpringBoot 2.1 | 第三十五篇:实战Aparche Maven 的核心概念与理论 Maven仓库管理 从入门到精通
  5. android 转场动画 4.4,Android高级UI开发(二十七)Material Design之转场动画(一)
  6. 2021-09-02编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary) 。
  7. 相干检测--概念,原理,科斯塔斯环
  8. 数学建模——层次分析法
  9. 凸优化有关的数值线性代数知识 3LU Cholesky和LDL因式分解
  10. 离散数学复习--第一章:命题逻辑