前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >8. MyBatis嵌套查询 - 一对一 - 一对多

8. MyBatis嵌套查询 - 一对一 - 一对多

作者头像
Devops海洋的渔夫
发布2022-01-17 14:41:19
1.8K0
发布2022-01-17 14:41:19
举报
文章被收录于专栏:Devops专栏Devops专栏

8. MyBatis嵌套查询 - 一对一 - 一对多

前言

在上一篇中我们已经基本认识了 MyBatis 的多表查询了。那么 MyBatis 还有一个比较有意思的功能,就是 嵌套查询。 这个功能有些类似 SQL 的多表联合查询,但是又不是。下面我们来认识一下。

什么是嵌套查询

嵌套查询就是将原来多表的联合查询语句拆成 多个单表的查询,再使用mybatis的语法嵌套在一起。

举个栗子

代码语言:javascript
复制
* 需求:查询一个订单,与此同时查询出该订单所属的用户

* 1. SQL 关联查询:
  select * from orders o inner join user u on o.uid = u.id where o.id = 1;
  
    * 缺点:
            sql语句编写难度大
            如果表中数据量大,笛卡尔积数量倍增,可能造成内存溢出
  
* 2. MyBatis 嵌套查询:
 a.根据订单id查询订单表
  select * from orders where id = 1; 
  // 查到订单id=1的订单信息 (uid=41)
  // 结果映射到 orders对象中
 b.再根据订单表中uid(外键)查询用户表
  select * from user where id = 订单表uid;
  // 结果映射到 orders.user 中
 c.最后由mybatis框架进行嵌套组合
  
  跟子查询的区别
   1. 嵌套查询分别执行两句sql : 又有订单信息,又有对应的用户信息
   2. 子查询执行一句 : 只能查到 订单编号为1的所属的用户信息
    select * from user where id = (select uid from orders where id = 1) 
    * 优点:
            sql语句编写简单
            没有多表关联,不会产生笛卡尔积, 特别是在表数据比较多的情况, 更有优势

看了上面的说明之后,下面来写写具体案例。

一对一【嵌套查询】

需求

需求:查询一个订单,与此同时查询出该订单所属的用户

sql语句

代码语言:javascript
复制
-- 1.根据订单id查询订单表
 select * from orders where id = 1;
-- 2.再根据订单表中uid(外键)查询用户表
 select * from user where id = 41;

执行过程:

下面我们来看看具体实现代码。

案例实现

1. 编写OrderMapper接口:编写查询 Orders 的接口方法 findOrderByIdWithUser
代码语言:javascript
复制
public interface OrdersMapper {

    /*
      一对一关系
        # 查询id=?的订单以及对应的用户
             select * from orders o inner join user u
               on o.uid = u.id
                   where o.id = ?;

            1. 参数类型: Integer id
            2. 返回值类型  Orders

        # 修改成嵌套查询
            1. 先查订单id=1 的订单信息  (uid=41)
                select * from orders where id = ? // 订单id
            2. 查uid=41的用户信息
                select * from user where id = ?  // 用户id

            3. 两个结果嵌套一起: 映射到 orders
    * */
    Orders findOrderByIdWithUser(Integer id);

}
2. 编写OrderMapper映射:映射 findOrderByIdWithUser 查询方法
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lijw.dao.OrdersMapper">

    <!--
        设置查询结果集 resultMap, 结果映射为 Orders 类
    -->
    <resultMap id="myorder" type="Orders" autoMapping="true">
        <!--  设置 id 字段映射 Orders 类的属性 id -->
        <id property="id" column="id"/>
        <!--  设置普通字段 ordertime 映射 Orders 类的属性 ordertime   -->
        <result property="ordertime" column="ordertime"/>
        <!--
            # 嵌套查询重点:
                0. 目的
                    select * from user where id = ?
                        映射到 orders.user属性中
                1. 编写查询user表的语句:
                    UserMapper.findUserById -> UserMapper.xml
                2. 嵌套到这里
                    association标签的两个属性
                        a. column : 条件 (执行查询方法的参数)
                         b. select : 调用第二句sql执行
                            接口的权限定名.方法名
                            UserMapper.findUserById(用户id)
        -->
        <association property="user" javaType="user" autoMapping="true"
                     column="uid"
                     select="com.lijw.dao.UserMapper.findUserById">
            <id property="id" column="uid"/>
            <result property="username" column="username"/>
        </association>
    </resultMap>

    <select id="findOrderByIdWithUser" resultMap="myorder">
        select * from orders where id = #{id}
    </select>
    
</mapper>
3. 编写 UserMapper接口:设置根据 id 查询 user 表的方法
代码语言:javascript
复制
public interface UserMapper {

    /*
    *  select * from user where id = ?
    *  1. 参数: Integer id 用户标号
    *  2. 返回值: User
    * */
    User findUserById(Integer id);
}
4. UserMapper映射:编写映射 SQL
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
实体类的映射文件
namespace 指定接口的类全名
-->
<mapper namespace="com.lijw.dao.UserMapper">

    <select id="findUserById" resultType="user">
        select * from user where id = #{id}
    </select>
</mapper>
5. 通过mybatis进行嵌套组合
代码语言:javascript
复制
看 OrderMapper.xml里的嵌套设置

一对一在结果映射 resultMap 中使用 association 进行嵌套查询,column 为传递的从 Orders 数据中查询出来的 uid.

6. 测试
代码语言:javascript
复制
public class OrderMapperTest{ 
    @Test
    public void test02(){
        SqlSession session = MyBatisUtil.getSqlSession();

        OrdersMapper mapper = session.getMapper(OrdersMapper.class);
        // 订单编号 = 1
        Orders orders = mapper.findOrderByIdWithUser(1);
        System.out.println("订单:" + orders);

        MyBatisUtil.commitAndClose(session);
    }
}

一对多【嵌套查询】

需求

需求:查询一个用户,与此同时查询出该用户具有的订单

sql语句

代码语言:javascript
复制
-- 1. 先根据用户id,查询用户表(一个)
SELECT * FROM `user` WHERE id = 41;
-- 2. 再根据用户id,查询订单表(多个)
SELECT * FROM orders WHERE uid = 41;

执行如下:

案例实现

1. OrderMapper接口:编写根据 id 查询该用户 User 下的订单 Orders 的方法
代码语言:javascript
复制
public interface OrderMapper {

    /** 
     * # 查询id=? 的用户以及拥有的订单
     * select * from user u inner join orders o
             on u.id = o.uid
                 where u.id = ?;
     * 1. 参数类型: Integer
     * 2. 返回值类型: User
     * <p>
     * # 修改成嵌套查询
     * a. 查询用户id=? 的用户
     * select * from user where user.id = ?;
     * <p>
     * b. 查询该用户的订单信息
     * select * from orders where uid = ?;
     * <p>
     * c. 嵌套
     */
    User findUserByIdWithOrders(Integer uid);
    
}
2. OrderMapper映射:编写映射 SQL
代码语言:javascript
复制
    <!--  根据id查询订单Orders的用户信息  -->
    <resultMap id="myuser" type="user" autoMapping="true">
        <id property="id" column="id"/>
        <!--
            collection: 为一对多的查询存储方式
                查询对应的订单信息  ->  user.list中
                column : 条件(结果集字段作为查询语句的参数)
        -->
        <collection property="list" ofType="orders" autoMapping="true"
                    column="id"
                    select="com.lijw.dao.OrdersMapper.findOrdersByUid">
            <id property="id" column="oid"/>
        </collection>
    </resultMap>

    <select id="findUserByIdWithOrders" resultMap="myuser">
        select * from user where user.id = #{uid}
    </select>
3. 编写第二句sql的接口和映射文件
代码语言:javascript
复制
 public interface OrderMapper {
 /*
    *  select * from orders where uid = ?;
    *
    *  参数类型: Integer id
    *  返回值类型: List<Orders>
    * */
    List<Orders> findOrdersByUid(Integer uid);
 }     
   
代码语言:javascript
复制
    <!--  根据uid查询订单信息  -->
    <select id="findOrdersByUid" resultType="orders">
        select * from orders where uid = #{uid}
    </select>
4. 通过mybatis进行嵌套组合
代码语言:javascript
复制
查看OrderMapper.xml文件
5. 测试
代码语言:javascript
复制
public class UserMapperTest extends BaseMapperTest {

    // 一对多嵌套查询测试
    @Test
    public void test03(){
        SqlSession session = MyBatisUtil.getSqlSession();

        OrdersMapper mapper = session.getMapper(OrdersMapper.class);
        // 查询用户id=41的用户以及拥有的订单
        User user = mapper.findUserByIdWithOrders(41);
        System.out.println(user);

        MyBatisUtil.commitAndClose(session);
    }

}

小结

代码语言:javascript
复制
* 步骤:一对多举例
 1)先查询(一方)单表
 2)再查询(多方)单表
 3)最后由mybatis嵌套组合

一对一配置:使用<resultMap>+<association>做配置,通过column条件,执行select查询

一对多配置:使用<resultMap>+<collection>做配置,通过column条件,执行select查询

优点:1.简化sql语句编写、2.不会产生笛卡尔积

缺点: 执行两遍

开发中到底使用哪一种?
 传统开发,数据量小:使用联合查询(执行一次,传输一次)
 互联网开发,数据量大:使用嵌套查询 (执行两次,但是只需要传输一次)
  当前也有人这么玩(知道): 
   在java中先查用户,在查角色,不在使用嵌套....(执行两次,需要传输两次)
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 海洋的渔夫 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 8. MyBatis嵌套查询 - 一对一 - 一对多
    • 前言
      • 什么是嵌套查询
        • 一对一【嵌套查询】
          • 需求
          • sql语句
          • 案例实现
        • 一对多【嵌套查询】
          • 需求
          • sql语句
          • 案例实现
        • 小结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档