1. 多表查询

上篇博客中,我们示例的2个查询都是单表查询,但实际的业务场景肯定是需要多表查询的,比如现在有个需求:

查询某个用户拥有的所有角色。这个需求要涉及到sys_user,sys_user_role,sys_role三张表,如何实现呢?

首先,在SysUserMapper接口中定义如下方法。

/**
* 根据用户id获取角色信息
*
* @param userId
* @return
*/
List<SysRole> selectRolesByUserId(Long userId);

然后打开对应的SysUserMapper.xml文件,添加如下select语句:

<select id="selectRolesByUserId" resultType="com.zwwhnly.mybatisaction.model.SysRole">
SELECT r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time createTime
FROM sys_user u
INNER JOIN sys_user_role ur ON u.id = ur.user_id
INNER JOIN sys_role r ON ur.role_id = r.id
WHERE u.id = #{userId}
</select>

细心的读者可能会发现,我们虽然使用到了多表查询,但是resultType设置的仍然是单表,即只包含角色表的信息。

如果我希望这个查询语句同时返回SysUser表的user_name字段呢,该如何设置resultType?

方法1:直接在SysRole实体类中添加userName字段。

private String userName;

public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
}

此时resultType不用修改。

方法2:新建扩展类,在扩展类中添加userName字段。

package com.zwwhnly.mybatisaction.model;

public class SysRoleExtend extends SysRole {
private String userName; public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
}
}

此时需要将resultType修改为:com.zwwhnly.mybatisaction.model.SysRoleExtend。

这种方式比较适合需要少量额外字段的场景。如果需要其他表的大量字段,可以使用方式3或者方式4,个人推荐使用方式4。

方法3:在SysRole实体类中添加SysUser类型的字段。

private SysUser sysUser;

public SysUser getSysUser() {
return sysUser;
} public void setSysUser(SysUser sysUser) {
this.sysUser = sysUser;
}

此时resultType不用修改。

方法4(推荐使用):新建扩展类,在扩展类中添加SysUser类型的字段。

书中推荐的是方式3,方式4是我个人认为更好的方式,因为实体类一般由工具自动生成,增加了字段后,后续容易忘记导致被覆盖掉。

package com.zwwhnly.mybatisaction.model;

public class SysRoleExtend extends SysRole {
private SysUser sysUser; public SysUser getSysUser() {
return sysUser;
} public void setSysUser(SysUser sysUser) {
this.sysUser = sysUser;
}
}

此时需要将resultType修改为:com.zwwhnly.mybatisaction.model.SysRoleExtend。

此时xml中的查询语句如下。

<select id="selectRolesByUserId" resultType="com.zwwhnly.mybatisaction.model.SysRoleExtend">
SELECT r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time createTime,
u.user_name "sysUser.userName",
u.user_email "sysUser.userEmail"
FROM sys_user u
INNER JOIN sys_user_role ur ON u.id = ur.user_id
INNER JOIN sys_role r ON ur.role_id = r.id
WHERE u.id = #{userId}
</select>

在SysUserMapperTest中添加测试代码如下。

@Test
public void testSelectRolesByUserId() {
SqlSession sqlSession = getSqlSession(); try {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class); List<SysRole> sysRoleList = sysUserMapper.selectRolesByUserId(1L); Assert.assertNotNull(sysRoleList);
Assert.assertTrue(sysRoleList.size() > 0);
} finally {
sqlSession.close();
}
}

运行该测试方法,输入日志如下。

DEBUG [main] - ==> Preparing: SELECT r.id, r.role_name roleName, r.enabled, r.create_by createBy, r.create_time createTime, u.user_name "sysUser.userName", u.user_email "sysUser.userEmail" FROM sys_user u INNER JOIN sys_user_role ur ON u.id = ur.user_id INNER JOIN sys_role r ON ur.role_id = r.id WHERE u.id = ?

DEBUG [main] - ==> Parameters: 1(Long)

TRACE [main] - <== Columns: id, roleName, enabled, createBy, createTime, sysUser.userName, sysUser.userEmail

TRACE [main] - <== Row: 1, 管理员, 1, 1, 2019-06-27 18:21:12.0, admin, admin@mybatis.tk

TRACE [main] - <== Row: 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0, admin, admin@mybatis.tk

DEBUG [main] - <== Total: 2

2. 多个接口参数的用法

2.1 参数类型是基本类型

截止目前,我们定义的方法都只有1个参数,要么是只有1个基本类型的参数,比如selectById(Long id);。

要么是只有1个对象作为参数,即将多个参数合并成了1个对象。

但有些场景下,比如只有2个参数,没有必要为这2个参数再新建一个对象,比如我们现在需要根据用户的id和角色的状态来获取用户的所有角色,那么该如何使用呢?

首先,在接口SysUserMapper中添加如下方法。

/**
* 根据用户id和角色的enabled状态获取用户的角色
*
* @param userId
* @param enabled
* @return
*/
List<SysRole> selectRolesByUserIdAndRoleEnabled(Long userId,Integer enabled);

然后,打开对应的SysUserMapper.xml文件,添加如下代码。

<select id="selectRolesByUserIdAndRoleEnabled" resultType="com.zwwhnly.mybatisaction.model.SysRole">
SELECT r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time createTime
FROM sys_user u
INNER JOIN sys_user_role ur ON u.id = ur.user_id
INNER JOIN sys_role r ON ur.role_id = r.id
WHERE u.id = #{userId} AND r.enabled = #{enabled}
</select>

在SysUserMapperTest测试类中,添加如下测试方法。

@Test
public void testselectRolesByUserIdAndRoleEnabled() {
SqlSession sqlSession = getSqlSession(); try {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
List<SysRole> sysRoleList = sysUserMapper.selectRolesByUserIdAndRoleEnabled(1L, 1); Assert.assertNotNull(sysRoleList);
Assert.assertTrue(sysRoleList.size() > 0);
} finally {
sqlSession.rollback();
sqlSession.close();
}
}

运行该测试方法,发现报如下错误。

报错信息中说未找到参数userId,可用的参数是[0,1,param1,param2],也就是说我们将代码修改为:

WHERE u.id = #{0} AND r.enabled = #{1}

或者修改为:

WHERE u.id = #{param1} AND r.enabled = #{param2}

这么使用是可以测试通过的,不过这样使用,代码阅读起来不够友好,因此并不推荐这么使用。

推荐在接口方法的参数前添加@Param注解,如下所示:

/**
* 根据用户id和角色的enabled状态获取用户的角色
*
* @param userId
* @param enabled
* @return
*/
List<SysRole> selectRolesByUserIdAndRoleEnabled(@Param("userId") Long userId, @Param("enabled") Integer enabled);

运行刚刚添加的测试方法,测试通过,输出日志如下:

DEBUG [main] - ==> Preparing: SELECT r.id, r.role_name roleName, r.enabled, r.create_by createBy, r.create_time createTime FROM sys_user u INNER JOIN sys_user_role ur ON u.id = ur.user_id INNER JOIN sys_role r ON ur.role_id = r.id WHERE u.id = ? AND r.enabled = ?

DEBUG [main] - ==> Parameters: 1(Long), 1(Integer)

TRACE [main] - <== Columns: id, roleName, enabled, createBy, createTime

TRACE [main] - <== Row: 1, 管理员, 1, 1, 2019-06-27 18:21:12.0

TRACE [main] - <== Row: 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0

DEBUG [main] - <== Total: 2

2.2 参数类型是对象

为了演示参数类型是对象的使用方法,我们在接口SysUserMapper中添加如下方法:

/**
* 根据用户id和角色的enabled状态获取用户的角色
*
* @param user
* @param role
* @return
*/
List<SysRole> selectRolesByUserAndRole(@Param("user") SysUser user, @Param("role") SysRole role);

此时对应的xml中的语句为:

<select id="selectRolesByUserAndRole" resultType="com.zwwhnly.mybatisaction.model.SysRole">
SELECT r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time createTime
FROM sys_user u
INNER JOIN sys_user_role ur ON u.id = ur.user_id
INNER JOIN sys_role r ON ur.role_id = r.id
WHERE u.id = #{user.id} AND r.enabled = #{role.enabled}
</select>

3. 源码及参考

源码地址:https://github.com/zwwhnly/mybatis-action.git,欢迎下载。

刘增辉《MyBatis从入门到精通》

原创不易,如果觉得文章能学到东西的话,欢迎点个赞、评个论、关个注,这是我坚持写作的最大动力。

如果有兴趣,欢迎添加我的微信:zwwhnly,等你来聊技术、职场、工作等话题(PS:我是一名奋斗在上海的程序员)。

MyBatis从入门到精通(三):MyBatis XML方式的基本用法之多表查询的更多相关文章

  1. MyBatis从入门到精通:使用XML方式(映射文件之类的)

    2.3节笔记部分: package tk.mybatis.simple; public class Temp { } /* 2.2 使用XML方式 MyBatis使用了Java的动态代理可以直接通过接 ...

  2. MyBatis从入门到精通(二):MyBatis XML方式的基本用法之Select

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. 明确需求 书中提到的需求是一个基 ...

  3. MyBatis从入门到精通(四):MyBatis XML方式的基本用法之增删改

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. insert用法 1.1 简单的 ...

  4. MyBatis从入门到精通(2):MyBatis XML方式的基本用法

    本章将通过完成权限管理的常见业务来学习 MyBatis XML方式的基本用法 2.1一个简单的权限控制需求 权限管理的需求: 一个用户拥有若干角色,一个角色拥有若干权限,权限就是对某个模块资源的某种操 ...

  5. MyBatis从入门到精通(第5章):MyBatis代码生成器

    jdk1.8.MyBatis3.4.6.MySQL数据库5.6.45.Eclipse Version: 2019-12 M2 (4.14.0) MyBatis从入门到精通(第5章):MyBatis代码 ...

  6. MyBatis从入门到精通(一):MyBatis入门

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. MyBatis简介 ​ 2001 ...

  7. MyBatis从入门到精通(五):MyBatis 注解方式的基本用法

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. @Select 注解 1.1 使 ...

  8. MyBatis从入门到精通(六):MyBatis动态Sql之if标签的用法

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解如何使用if标签生成动 ...

  9. MyBatis从入门到精通(七):MyBatis动态Sql之choose,where,set标签的用法

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解如何使用choose, ...

随机推荐

  1. WPF的页面导航

    工作中之前接触过的WPF程序一直是使用TabControl作不同页面间的切换,每个Tab负责独立的功能,清晰简捷,所以一直就没有动力研究WPF自带的页面导航.(虽然接触过使用页面导航的WPF项目,也并 ...

  2. Windows下Apache+MySQL+PHP快速配置的几种方法

    Apache MySQL PHP Windows WAMP 1.易思EasySiteServer服务器集成环境 v1.0  (推荐) 尔创互联为推广其ESPCMS而开发的一个小东东,很好用.零配置,完 ...

  3. VS2005 编译 Qt4.8.2库,并修正源码中的错误

    从昨天开始利用VS2005对Qt4.8.2库进行编译,在编译到某个文件时,总是报错,提示VS的cl.exe和nmake.exe返回致命错误.错误罗列如下: DefaultLocalizationStr ...

  4. Leetcode 268 Missing Number 位运算

    题意:先将0, 1, 2, ..., n放入数组,然后去掉其中一个值,找到那个值. 这题与singe number 是一个类型,变形的地方就是首先需要将0, 1, 2, ..., n再次放入这个数组, ...

  5. 可视化利器 —— t-SNE(matlab toolbox 的使用与解释)

    t-SNE – Laurens van der Maaten(感谢学术男神们的无私开源) User_guide.pdf(用户指南) 1. tsne 函数 mappedX = tsne(X, label ...

  6. ASP.NET Core MVC 设计模式 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core MVC 设计模式 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core MVC 设计模式 上一章节中,我们提到 ASP.NET Co ...

  7. Distinct去除重复项

    之前在做权限模块时,因不同角色可能拥有相同的菜单,导致呈现在浏览器上时出现重复菜单项,所以需要在获取用户拥有菜单项时需要过滤重复项, 用到了Distinct,两个重载 public static IQ ...

  8. 关于C#你应该知道的2000件事

    原文 关于C#你应该知道的2000件事 下面列出了迄今为止你应该了解的关于C#博客的2000件事的所有帖子. 帖子总数= 1,219 大会 #11 -检查IL使用程序Ildasm.exe d #179 ...

  9. 专访Jeffrey Richter:Windows 8是微软的重中之重

    Jeffrey Richter 以其多本 Windows 核心技术的经典著作而闻名,同时,他深入掌握微软的 .NET 等一系列核心技术,他所创办的 Wintellect 公司与微软有密切的合作关系,他 ...

  10. EF延迟加载LazyLoading

    优点 只在需要的时候加载数据,不需要预先计划,避免了各种复杂的外连接.索引.视图操作带来的低效率问题 缺陷:多次与DB交互,性能降低 阻止延迟加载解决方案:1.ToList(),返回的东西是个内存级的 ...