mybatis查询树形数据的两种方法

原贴地址: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 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 getMenulist() {
21         return menulist;
22     }
23     public void setMenulist(List 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 
 2         
 3         
 4         
 5             
 6         
 7     
 8     
 9     
12     
13     

复制代码

复制代码

 1     @Test
 2     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 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 getChildren() {
21         return children;
22     }
23     public void setChildren(List children) {
24         this.children = children;
25     }
26     
27     
28 }

复制代码

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

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

 

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

复制代码

 1 
 2         
 3         
 4         
 5             
 6             
 7             
 8             
 9             
10             
11         
12     
13     
14     

复制代码

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

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

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

  

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

  

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

  

 

你可能感兴趣的:(数据库)