MyBatis 多表查询


MyBatis 多表查询

1. 表关系

  • 在关系型数据库当中,表关系分为三种:
    在这里插入图片描述
  • 数据建立表关系,通过主外键关联;而实体建立关系,通过属性关联。
  • 多对一情况:一个订单只能从属于一个用户,MyBatis 框架就把这个多对一看做成一对一来实现。
    在这里插入图片描述

2. 环境搭建

在这里插入图片描述

3. 一对一(多对一)

  • 一对一查询模型:用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户。
  • 一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户。
    在这里插入图片描述

a. 实体和表映射关系

SELECT * FROM orders o INNER JOIN `user` u ON o.`uid` = u.`id` WHERE o.`id` = 1

在这里插入图片描述

  • Order 是 SQL 的关键字,取名 orders 是为了避免冲突。

b. Order 实体类

package com.regino.domain;

import java.util.Date;

// 订单实体类
public class Order {

    private Integer id;

    private Date ordertime;

    private Double money;

    // 一个订单从属于一个用户
    private User user;
}
  • 实体类全部省略 set/get/toString 方法,一经修改需要重写这些方法。

c. OrderMapper 接口

package com.regino.dao;

import com.regino.domain.Order;

public interface OrderMapper {

    // 一对一关联查询
    public Order findByIdWithUser(Integer id);
}

d. OrderMapper.xml

<?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.regino.dao.OrderMapper">


    <resultMap id="orderMap" type="Order">
        <id column="id" property="id"></id>
        <result column="ordertime" property="ordertime"></result>
        <result column="money" property="money"></result>
        <!--
            一对一多表关联 association标签
                 property="user" 关联实体的属性名
                 javaType="com.regino.domain.User" 关联实体java类型
        -->
        <association property="user" javaType="User">
            <id column="uid" property="id"></id>
            <result column="username" property="username"></result>
            <result column="birthday" property="birthday"></result>
            <result column="sex" property="sex"></result>
            <result column="address" property="address"></result>
        </association>
    </resultMap>

    <!--
        一对一关联查询
            resultType:单表映射封装
            resultMap:多表查询必须手动映射封装
    -->
    <select id="findByIdWithUser" parameterType="int" resultMap="orderMap">
        SELECT * FROM orders o INNER JOIN `user` u ON o.`uid` = u.`id` WHERE o.`id` = #{id}
    </select>


</mapper>
  • 创建 Mapper 后要在 SqlMapConfig.xml 中加载对应的 Mapper 的映射文件。
    <!--加载映射文件-->
    <mappers>
        <mapper resource="com/regino/dao/UserMapper.xml"></mapper>
        <mapper resource="com/regino/dao/OrderMapper.xml"></mapper>
        <mapper resource="com/regino/dao/RoleMapper.xml"></mapper>
    </mappers>

e. 测试

package com.regino.service;

import com.regino.dao.OrderMapper;
import com.regino.domain.Order;
import com.regino.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class OrderMapperTest {

    private SqlSession sqlSession = null;

    // 此方法在测试方法执行之前,执行
    @Before
    public void before() {
        // 获取sqlSession对象
        sqlSession = MyBatisUtils.openSession();// 此方法必须线程内独享....
    }

    // 此方法在测试地方法执行之后,执行
    @After
    public void after() {
        // 关闭sqlSession
        MyBatisUtils.close(sqlSession);
    }


    // 一对一关联测试
    @Test
    public void test01()throws Exception{
        // 获取代理对象
        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);

        // 根据id查询
        Order order = orderMapper.findByIdWithUser(1);
        System.out.println(order);
    }
}

4. 一对多

  • 一对多查询模型:用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户。
  • 一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单。
    在这里插入图片描述

a. 实体和表关系

SELECT *,o.id AS oid FROM `user` u INNER JOIN orders o ON u.`id` = o.`uid` WHERE u.`id`=41

在这里插入图片描述

b. User 实体类

package com.regino.domain;

import java.util.Date;
import java.util.List;

public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一个用户具有多个订单
    private List<Order> orderList;
}

c. UserMapper 接口

package com.regino.dao;

import com.regino.domain.User;

public interface UserMapper {

    // 一对多关联
    public User findByIdWithOrders(Integer id);
}

d. UserMapper.xml

<?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.regino.dao.UserMapper">
    <resultMap id="userMap" type="User">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>
        <!--
            一对多关联 collection标签
                property="orderList" 关联实体集合的属性名
                ofType="com.regino.domain.Order" 关联实体的java类型(集合泛型的类型)
        -->
        <collection property="orderList" ofType="com.regino.domain.Order">
            <id column="oid" property="id"></id>
            <result column="ordertime" property="ordertime"></result>
            <result column="money" property="money"></result>
        </collection>
    </resultMap>

    <!--
        一对多关联
    -->
    <select id="findByIdWithOrders" parameterType="int" resultMap="userMap">
       SELECT *,o.id AS oid FROM `user` u INNER JOIN orders o ON u.`id` = o.`uid` WHERE u.`id`=#{id}
    </select>


</mapper>

e. 测试

public class UserMapperTest extends BaseMapperTest {

    // 一对多测试
    @Test
    public void test01() throws Exception {
        // 获取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = userMapper.findByIdWithOrders(41);

        System.out.println(user);
    }
}

5. 多对多(由两个一对多组成)

  • 多对多查询的模型:用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用。
  • 多对多查询的需求:查询 用户 同时查询出该用户的所有角色。
  • 在 MyBatis 中多对多实现,跟一对多步骤是一样,区别就在于 SQL 语句
    在这里插入图片描述

a. 实体和表关系

SELECT * FROM `user` u 
	INNER JOIN user_role ur ON u.`id` = ur.`uid`  -- 用户连接中间表
	INNER JOIN role r ON ur.`rid` = r.`id`  -- 再根据中间表连接角色
	WHERE u.id = 41 -- 用户id 作为条件

在这里插入图片描述

b. User 和 Role 实体

package com.regino.domain;

public class Role {

    private Integer id;

    private String roleName;

    private String roleDesc;
}
package com.regino.domain;

import java.util.Date;
import java.util.List;

public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一个用户具有多个角色
    private List<Role> roleList;
}

c. UserMapper 接口

    // 多对多关联
    public User findByIdWithRoles(Integer id);

d. UserMapper.xml

    <resultMap id="userWithRoleMap" type="User">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>
        <!--
            多对多实现步骤和一对多是一样的(区别在于sql语句)
        -->
        <collection property="roleList" ofType="com.regino.domain.Role">
            <id column="rid" property="id"></id>
            <result column="role_name" property="roleName"></result>
            <result column="role_desc" property="roleDesc"></result>
        </collection>
    </resultMap>

    <!--
        多对多关联
    -->
    <select id="findByIdWithRoles" parameterType="int" resultMap="userWithRoleMap">
        SELECT * FROM `user` u
            INNER JOIN user_role ur ON u.`id` = ur.`uid`  -- 用户连接中间表
            INNER JOIN role r ON ur.`rid` = r.`id`  -- 再根据中间表连接角色
            WHERE u.id = #{id} -- 用户id 作为条件
    </select>

e. 测试

    @Test
    public void test02()throws Exception{
        // 获取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = userMapper.findByIdWithRoles(41);

        System.out.println(user);
    }

6. 多对多补充

  • 以角色为中心,查询多个用户。

a. 实体和表关系

SELECT * FROM `role` r
	INNER JOIN user_role ur ON r.`id` = ur.`rid`  -- 角色连接中间表
	INNER JOIN USER u ON ur.`uid` = u.`id`  -- 再根据中间表连接用户
	WHERE r.id = #{id} -- 角色id 作为条件

b. User 和 Role 实体

package com.regino.domain;

public class Role {

    private Integer id;

    private String roleName;

    private String roleDesc;

    // 一个角色具有多个用户
    private List<User> userList;
}
package com.regino.domain;

import java.util.Date;
import java.util.List;

public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一个用户具有多个角色
    private List<Role> roleList;
}

c. RoleMapper 接口

    // 多对多关联
    public User findByIdWithRoles(Integer id);

d. RoleMapper.xml

    <resultMap id="roleWithUserMap" type="Role">
        <id column="id" property="id"></id>
        <result column="role_name" property="roleName"></result>
        <result column="role_desc" property="roleDesc"></result>

        <!--
            多对多实现步骤和一对多是一样的(区别在于sql语句)
        -->
        <collection property="userList" ofType="com.regino.domain.User">
            <id column="rid" property="id"></id>
            <result column="username" property="username"></result>
            <result column="birthday" property="birthday"></result>
            <result column="sex" property="sex"></result>
            <result column="address" property="address"></result>
        </collection>
    </resultMap>

    <!--
        多对多关联
    -->
    <select id="findByIdWithUsers" parameterType="int" resultMap="roleWithUserMap">
        SELECT * FROM `role` r
            INNER JOIN user_role ur ON r.`id` = ur.`rid`  -- 角色连接中间表
            INNER JOIN USER u ON ur.`uid` = u.`id`  -- 再根据中间表连接用户
            WHERE r.id = #{id} -- 角色id 作为条件
    </select>

e. 测试

package com.regino.service;

import com.regino.dao.RoleMapper;
import com.regino.domain.Role;
import com.regino.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class RoleMapperTest {

    private SqlSession sqlSession = null;

    // 此方法在测试方法执行之前,执行
    @Before
    public void before() {
        // 获取sqlSession对象
        sqlSession = MyBatisUtils.openSession();// 此方法必须线程内独享....
    }

    // 此方法在测试地方法执行之后,执行
    @After
    public void after() {
        // 关闭sqlSession
        MyBatisUtils.close(sqlSession);
    }
    
    // 多对多测试2(根据角色查询用户)
    @Test
    public void test03()throws Exception{
        // 获取代理
        RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);

        Role role = roleMapper.findByIdWithUsers(1);

        System.out.println(role);
    }
}

7. 总结

  1. 一对一配置:使用 <resultMap> + <association> 做配置。
    association:封装单个对象的属性。
    • property:关联的实体属性名
    • javaType:关联的实体类型(别名)
  2. 一对多配置:使用 <resultMap> + <collection> 做配置。
    collection:封装一个集合属性。
    • property:关联的集合属性名
    • ofType:关联的集合泛型类型(别名)
  3. 多对多配置:使用 <resultMap> + <collection> 做配置。
    collection:封装一个集合属性。
    • property:关联的集合属性名
    • ofType:关联的集合泛型类型(别名)
  • 多对多的配置跟一对多很相似,难度在于 SQL 语句的编写。

8. 优化测试

  • 增加了 BaseMapperTest 作为所有测试类的父类。
public class BaseMapperTest {

    protected SqlSession sqlSession = null;

    // 此方法在测试方法执行之前,执行
    @Before
    public void before() {
        // 获取sqlSession对象
        sqlSession = MyBatisUtils.openSession();// 此方法必须线程内独享....
    }

    // 此方法在测试地方法执行之后,执行
    @After
    public void after() {
        // 关闭sqlSession
        MyBatisUtils.close(sqlSession);
    }
}
  • 示例:OrderMapperTest 的一对一关联测试
package com.regino.service;

import com.regino.dao.OrderMapper;
import com.regino.domain.Order;
import org.junit.Test;

public class OrderMapperTest extends BaseMapperTest { // 继承父类,就可以直接使用 父类的方法和成员变量了

    // 一对一关联测试
    @Test
    public void test01() throws Exception {
        // 获取代理对象
        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);

        // 根据id查询
        Order order = orderMapper.findByIdWithUser(1);
        System.out.println(order);
    }
}

原文链接:https://qwert.blog.csdn.net/article/details/106041824

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值