通过几个案例帮你轻松搞懂Mybatis 的多表关联查询

本文通过实例详细讲解了Mybatis中多表关联查询的处理,包括resultMap的基础使用、一对一、一对多、多对多关联查询,以及Mybatis的延迟加载策略,帮助读者深入理解Mybatis的映射配置和数据加载方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


一、搭建环境

1、创建项目

在这里插入图片描述

2、添加jar包

在这里插入图片描述

3、创建实体

public class Users {
    private int userid;
    private String username;
    private String usersex;

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userid=" + userid +
                ", username='" + username + '\'' +
                ", usersex='" + usersex + '\'' +
                '}';
    }
}

4、创建properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc.mysql://localhost:3306/bjsxt
jdbc.username=root
jdbc.password=mysql

5、添加工具类

public class MybatisUtils {
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();
    private static SqlSessionFactory sqlSessionFactory = null;
    static{
        //创建SqlSessionFactory
        InputStream is = null;
        try{
            is = Resources.getResourceAsStream("mybatis-cfg.xml");
        }catch (IOException e){
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    }

    //获取SqlSession
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession == null){
            sqlSession = sqlSessionFactory.openSession();
            threadLocal.set(sqlSession);
        }
        return sqlSession;
    }

    //关闭SqlSession
    public static void closeSqlSession(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession != null){
            sqlSession.close();
            threadLocal.set(null);
        }
    }
}

6、创建全局配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--    引入properties属性文件-->
    <properties resource="db.properties"/>

<!--    配置别名-->
    <typeAliases>
        <package name="com.bjsxt.pojo"/>
    </typeAliases>

<!--    配置环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    
<!--    引入映射配置文件-->
    <mappers>
        <package name="com.bjsxt.mapper"/>
    </mappers>

</configuration>

7、创建映射配置文件

<?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.bjsxt.mapper.UsersMapper">
</mapper>

二、手动处理映射关系

resultMap 标签是 Mybatis 最强大的元素,它可以将查询到的复杂数据(比如查询到几个表中数据)映射到一个结果集当中。

1、resultMap 的基础使用场景

在 Mybatis 中如果查询的结果集的列名与 POJO 的属性名相同,那么我们是不需要在Mybatis 中配置映射关系的,但是当查询到的结果集的列名与 POJO 的属性名不匹配时,Mybatis 是无法完成影射处理的。

解决方案:

  • 通过定义列别名的方式来解决该问题。
  • 通过在 resultMap 标签中定义映射关系来解决该问题。

2、通过 resultMap 标签解决实体与结果集的映射

2.1、 修改映射配置文件

    <resultMap id="usersMapper" type="com.bjsxt.pojo.Users">
        <id property="userid" column="id"/>
        <result property="username" column="name"/>
        <result property="usersex" column="sex"/>
    </resultMap>
    <select id="selectUsersAll" resultMap="usersMapper">
        select userid as id,username as name,usersex as sex from users
    </select>

2.2、 修改 UsersMapper 接口

public interface UsersMapper {
    List<Users> selectUsersAll();
}

2.3、 创建测试类

public class SelectUsersAllTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
        List<Users> list = usersMapper.selectUsersAll();
        list.forEach(System.out::println);
    }
}

三、 一对一的关联查询

1、标签

标签是处理单一的关联对象(处理单一属性的关联关系)。
property:指定关联对象的属性
javaType:关联对象的类型(可以省略)
select:执行一个新的查询
column:在新的查询中用哪个列的值作为查询条件

2、需求

完成用户与角色查询。要求一个用户只能对应一个角色。

3、创建 roles 表

CREATE TABLE `roles` (
  `roleid` int(11) NOT NULL AUTO_INCREMENT,
  `rolename` varchar(30) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`roleid`),
  UNIQUE KEY `roles_fk` (`user_id`) USING BTREE,
  CONSTRAINT `roles_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


4、创建 Roles 实体

public class Roles {
    private int roleid;
    private String rolename;

    public int getRoleid() {
        return roleid;
    }

    public void setRoleid(int roleid) {
        this.roleid = roleid;
    }

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }

    @Override
    public String toString() {
        return "Roles{" +
                "roleid=" + roleid +
                ", rolename='" + rolename + '\'' +
                '}';
    }
}

5、修改Users实体

public class Users {
    private int userid;
    private String username;
    private String usersex;
    private Roles roles;

    public Roles getRoles() {
        return roles;
    }

    public void setRoles(Roles roles) {
        this.roles = roles;
    }

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userid=" + userid +
                ", username='" + username + '\'' +
                ", usersex='" + usersex + '\'' +
                '}';
    }
}

6、修改映射配置文件

<!--    一对一的关联查询映射-->
    <resultMap id="usersAndRolesMapper" type="com.bjsxt.pojo.Users">
        <id property="userid" column="userid"/>
        <result property="username" column="username"/>
        <result property="usersex" column="usersex"/>
        <association property="roles" javaType="com.bjsxt.pojo.Roles">
            <id property="roleid" column="roleid"/>
            <result property="rolename" column="rolename"/>
        </association>
    </resultMap>
    <select id="selectUsersAndRoles" resultMap="usersAndRolesMapper">
        select * from users as u,roles as r where u.userid = r.user_id and u.userid = #{userid}
    </select>

7、修改UsersMapper接口

    Users selectUsersAndRoles(int userid);

8、创建测试类


public class SelectUsersAndRolesTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
        Users users = usersMapper.selectUsersAndRoles(1);
        System.out.println(users);
        Roles roles = users.getRoles();
        System.out.println(roles);
    }
}

9、运行结果

在这里插入图片描述

四、 一对多的关联查询

1、collection标签

在这里插入图片描述
在这里插入图片描述

2、需求

完成用户与订单查询。要求一个用户可以对应多个订单。

3、创建 Orders 表

CREATE TABLE `orders` (
  `orderid` int(11) NOT NULL AUTO_INCREMENT,
  `orderprice` double DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`orderid`),
  KEY `orders_fk` (`user_id`),
  CONSTRAINT `orders_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4、创建 Orders 实体

public class Orders {
    private int orderid;
    private double orderprice;

    public int getOrderid() {
        return orderid;
    }

    public void setOrderid(int orderid) {
        this.orderid = orderid;
    }

    public double getOrderprice() {
        return orderprice;
    }

    public void setOrderprice(double orderprice) {
        this.orderprice = orderprice;
    }


    @Override
    public String toString() {
        return "Orders{" +
                "orderid=" + orderid +
                ", orderprice=" + orderprice +
                '}';
    }
}

5、修改 Users 实体

public class Users {
    private int userid;
    private String username;
    private String usersex;
    private Roles roles;
    private List<Orders> orders;


    public List<Orders> getOrders() {
        return orders;
    }

    public void setOrders(List<Orders> orders) {
        this.orders = orders;
    }

    public Roles getRoles() {
        return roles;
    }

    public void setRoles(Roles roles) {
        this.roles = roles;
    }

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userid=" + userid +
                ", username='" + username + '\'' +
                ", usersex='" + usersex + '\'' +
                '}';
    }
}

6、修改映射配置文件

<!--    一对多映射关系-->
    <resultMap id="usersAndOrdersMapper" type="com.bjsxt.pojo.Users">
        <id property="userid" column="userid"/>
        <result property="username" column="username"/>
        <result property="usersex" column="usersex"/>
        <collection property="orders" ofType="com.bjsxt.pojo.Orders">
            <id property="orderid" column="orderid"/>
            <result property="orderprice" column="orderprice"/>
        </collection>
    </resultMap>
<!--    根据用户ID查询用户以及该用户所对应的所有订单-->
    <select id="selectUsersAndOrders" resultMap="usersAndOrdersMapper">
        select * from users as u,orders as o where u.userid = o.user_id and u.userid = #{userid}
    </select>

7、修改 UsersMapper 接口

    Users selectUsersAndOrders(int userid);

8、创建测试类

public class SelectUsersAndOrdersTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
        Users users = usersMapper.selectUsersAndOrders(1);
        System.out.println(users);
        List<Orders> list = users.getOrders();
        list.forEach(System.out::println);
    }
}

9、运行结果

在这里插入图片描述

五、 多对多的关联查询

1、需求

根据 ID 查询用户与订单以及订单中所包含的所有商品。

2、创建 items 表

CREATE TABLE `items` (
  `itemid` int(11) NOT NULL AUTO_INCREMENT,
  `itemname` varchar(255) DEFAULT NULL,
  `itemprice` double DEFAULT NULL,
  PRIMARY KEY (`itemid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

3、创建 orders_items 中间表

CREATE TABLE `orders_items` (
  `order_id` int(11) NOT NULL,
  `item_id` int(11) NOT NULL,
  PRIMARY KEY (`order_id`,`item_id`),
  KEY `order_item_fk2` (`item_id`),
  CONSTRAINT `order_item_fk1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`orderid`),
  CONSTRAINT `order_item_fk2` FOREIGN KEY (`item_id`) REFERENCES `items` (`itemid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4、创建 Items 实体

public class Items {
    private int itemid;
    private String itemname;
    private double itemprice;


    public int getItemid() {
        return itemid;
    }

    public void setItemid(int itemid) {
        this.itemid = itemid;
    }

    public String getItemname() {
        return itemname;
    }

    public void setItemname(String itemname) {
        this.itemname = itemname;
    }

    public double getItemprice() {
        return itemprice;
    }

    public void setItemprice(double itemprice) {
        this.itemprice = itemprice;
    }

    @Override
    public String toString() {
        return "Items{" +
                "itemid=" + itemid +
                ", itemname='" + itemname + '\'' +
                ", itemprice=" + itemprice +
                '}';
    }
}

5、修改 Orders 实体

public class Orders {
    private int orderid;
    private double orderprice;
    private List<Items> items;

    public List<Items> getItems() {
        return items;
    }

    public void setItems(List<Items> items) {
        this.items = items;
    }

    public int getOrderid() {
        return orderid;
    }

    public void setOrderid(int orderid) {
        this.orderid = orderid;
    }

    public double getOrderprice() {
        return orderprice;
    }

    public void setOrderprice(double orderprice) {
        this.orderprice = orderprice;
    }


    @Override
    public String toString() {
        return "Orders{" +
                "orderid=" + orderid +
                ", orderprice=" + orderprice +
                '}';
    }
}

6、修改映射配置文件

<!--    多对多关联查询-->
    <resultMap id="usersAndOrdersAndItems" type="com.bjsxt.pojo.Users">
        <id property="userid" column="userid"/>
        <result property="username" column="username"/>
        <result property="usersex" column="usersex"/>
        <collection property="orders" ofType="com.bjsxt.pojo.Orders">
            <id property="orderid" column="orderid"/>
            <result property="orderprice" column="orderprice"/>
            <collection property="items" ofType="com.bjsxt.pojo.Items">
                <id property="itemid" column="itemid"/>
                <result property="itemname" column="itemname"/>
                <result property="itemprice" column="itemprice"/>
            </collection>
        </collection>
    </resultMap>
<!--    根据用户ID查询用户与订单以及订单中所包含的商品-->
    <select id="selectUsersAndOrdersAndItems" resultMap="usersAndOrdersAndItems">
        select * from users as u,orders as o,orders_items as oi,items as i where u.userid = o.user_id and o.orderid = oi.order_id and oi.item_id = i.itemid and u.userid = #{userid}
    </select>

7、修改 UsersMapper 接口

    Users selectUsersAndOrdersAndItems(int userid);

8、创建测试类

public class SelectUsersAndOrdersAndItemsTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
        Users users = usersMapper.selectUsersAndOrdersAndItems(1);
        System.out.println(users);
        List<Orders> orders = users.getOrders();
        for (Orders order :orders){
            System.out.println(order);
            List<Items> items = order.getItems();
            items.forEach(System.out::println);
        }
    }
}

9、运行结果

在这里插入图片描述

六、 Mybatis 多表查询中的数据加载方式

1、多表查询 SQL 语句的写法

1.1、 连接查询

使用内连接或者外连接的方式查询数据。
优点:在一次查询中完成数据的查询操作。降低查询次数提高查询效率。
缺点:如果查询返回的结果集较多会消耗内存空间。

1.2、 N+1 次查询

分解式查询,将查询分解成多个 SQL 语句。
优点:配和着延迟加载可实现结果集分步获取,节省内存空间。
缺点:由于需要执行多次查询,相比连接查询效率低。

2、N+1 查询的加载方式

2.1、 立即加载

在一次查询中执行所有的 SQL 语句。

2.2、 延迟加载

在一次查询中执行部分 SQL 语句,根据操作映射的关联象触发其他查询。

3、MyBatis 的延迟加载看的使用

在 Mybatis 中可以使用延迟加载数据的策略实现对关联对象的查询。
使用延迟加载要求:

  • 查询方式需要使用 N+1 次查询。
  • 在映射配置文件或者全局配置文件中开启延迟加载。

3.1、开启延迟加载

在这里插入图片描述
在这里插入图片描述

3.2、 修改全局配置文件配置触发方法

<settings>
<setting name="lazyLoadTriggerMethods" value=""/>
</settings>

3.3、 修改映射配置文件

<!--    延迟加载-->
    <resultMap id="usersAndOrdersMapperLazy" type="com.bjsxt.pojo.Users">
        <id property="userid" column="userid"/>
        <result property="username" column="username"/>
        <result property="usersex" column="usersex"/>
        <collection property="orders" ofType="com.bjsxt.pojo.Orders" fetchType="lazy" select="selectOrdersByUserId" column="userid"/>

    </resultMap>
<!--    根据用户ID查询用户-->
    <select id="selectUsersByIdLazy" resultMap="usersAndOrdersMapperLazy">
        select * from users where userid = #{userid}
    </select>

<!--    根据用户ID查询订单-->
    <select id="selectOrdersByUserId" resultType="com.bjsxt.pojo.Orders">
        select * from orders where user_id = #{userid}
    </select>

3.4、 修改 UsersMapper 接口

 Users selectUsersByIdLazy(int userid);

3.5、 创建测试类

public class SelectUsersByIdLazyTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
        Users users = usersMapper.selectUsersByIdLazy(1);
        System.out.println(users);
        List<Orders> list = users.getOrders();
        list.forEach(System.out::println);
    }
}

3.6、 测试结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值