第四章 MyBatis复杂映射和配置

1. MyBatis高级查询

1.1 resultType和resultMap属性

1.1.1 resultType

查询结果返回的类型,返回的结果字段名一定是要相同的,不一致的话会映射不成功

<select id="findByLabelName" resultType="com.jin10.vo.LabelVo">
    select * from label where label_name = #{labelName}
</select>

1.1.2 resultMap

和上面的resultType相比,这个更加类似于自定义映射,可以解决复杂的映射问题,也可以解决上面字段不一致导致的映射不成功的问题

<!--id当前命名空间中的一个唯一标识,用于标识唯一一个resultMap,type表示类的完全限定名, 或者一个类型别名-->
<resultMap id="baseResultMap" type="com.jin10.vo.CategoriesVo">
    <!--主键id-->
    <id property="id" column="categories_id"></id>
    <!--数据库中普通字段结果映射-->
    <result property="groupName" column="categories_name"></result>
    <result property="createTime"  column="create_time"></result>
</resultMap>

1.2 多条件查询

三种方式进行多条件查询:1. 传递pojo对象(推荐);2. 使用注解@Param()获取参数;3. 使用序号进行代替

1.2.1 传递pojo对象

List<User> findByIdAndName(User user);
<select id="findByIdAndName" resultMap="baseResultMap" parameterType="com.lagou.domain.User">
    select * from user where id = #{user.id} and username = #{user.name};
</select>

1.2.2 使用注解进行获取参数

List<User> findByIdAndName(@Param("id") Integer id, @Param("name") String name);
<select id="findByIdAndName" resultMap="baseResultMap">
    select * from user where id = #{id} and username = #{name};
</select>

1.2.3 使用序号传递参数

List<User> findByIdAndName(Integer id, String name);
<select id="findByIdAndName" resultMap="baseResultMap">
    select * from user where id = #{arg0} and username = #{arg1};
    <!--或者下面这种方式-->
    select * from user where id = #{param1} and username = #{param2};
</select>

总结:相对比上面三种多条件查询方式,传递pojo对象的方式会更加的直观和方便,基于注解的方式也可以,第三种方式的话就适当的了解一下,不太推荐使用这种方式

1.3 模糊查询

1.3.1 方式一,使用#{}占位符进行查询

List<User> findByUsername(String username);
<select id="findByUsername" resultMap="baseResultMap" parameterType="string">
    select * from user where like #{name};
</select>
public void test() {
    userMapper.findByUserName("%用户名%");
}

1.3.2 方式二,使用${}进行查询

List<User> findByUsername(String username);
<select id="findByUsername" resultMap="baseResultMap" parameterType="string">
    select * from user where like '${name}';
</select>
public void test() {
    userMapper.findByUserName("%用户名%");
}

总结:#{}和${}两者之间的比较

  • {} 表示的是一个占位符,在转化为sql语句的时候会自动的添加上单引号

    • 实现preparedStatement向占位符中设置值,自动进行Java类型和JDBC类型转换,可以有效地防止SQL注入
    • 可以接收简单类型或者pojo属性值
    • 如果parameterType传递单个简单类型值,#{}括号中名称可以随便写
  • ${} 表示拼接字符串

    • 将parameterType传入的内容拼接在sql中且不进行JDBC类型转换,会出现sql注入的问题
    • 可以接收简单类型或者pojo属性值
    • 如果parameterType传递单个简单类型值,#{}括号中名称只能是value

2. 映射配置文件深入

2.1 返回主键

在应用场景中,经常会在插入数据之后进行返回当前主键id,Mybatis提供了两种返回插入数据的主键方式

2.1.1 方式一

public interface UserMapper() {
    int save(User user);
}
<!--
    useGeneratedKeys属性:声明返回主键
    keyProperty:把返回的主键值封装到实体的id属性中
-->
<insert id="save" parameterType="user" useGeneratedKeys="true" keyProperty="id"> 
    insert into 'user' (username, birthday, sex, address) values (#{user}, #{birthday}, #{sex}, #{address})
</insert>
public void test() {
    User insert = new User();
    insert.setUsername("测试名字");
    insert.setBirthday(new Date());
    insert.setSex("man");
    insert.setAddress("广东广州");
    userMapper.save(insert);
}

上面的这种方法不能满足所有的数据库,对于数据库本身没有自动生成主键的Oracle来说是没有作用的

2.1.2 方式二(适用于所有的数据库)

public interface UserMapper() {
    int save(User user);
}
<!--
    keyColumn属性:声明返回主键
    keyProperty:把返回的主键值封装到实体的id属性中
    resultType: 指定主键的类型
    order: 设置在SQL语句执行之前(后)执行该语句,Oracle设置order为BEFORE
-->
<insert id="save" parameterType="user">
    <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER"> 
        SELECT LAST_INSERT_ID();
    </selectKey> 
    insert into 'user' (username, birthday, sex, address) values (#{user}, #{birthday}, #{sex}, #{address})
</insert>
public void test() {
    User insert = new User();
    insert.setUsername("测试名字");
    insert.setBirthday(new Date());
    insert.setSex("man");
    insert.setAddress("广东广州");
    userMapper.save(insert);
}

2.2 动态SQL语句

动态SQL语句是MyBatis里面的一个重大特性

2.2.1 if标签(很常用)

List<User> findByIf(User user);
<select id="findByIf" parameterType="user" resultType="User">
    select * from user
    <!-- Where标签相当于where 1=1,如果没有条件,就不会拼接where关键字-->
    <where>
        <if test="id != null">
            AND id = #{id}
        </if>
        <if test="username != null">
            AND username = #{username}
        </if>
    </where>
</select>
public void test() {
    User select = new User();
    select.setId(10);
    select.setUsername("测试名字");
    userMapper.findByIf(select);
}

2.2.2 set标签

更新操作使用set可以免去一些不必要的麻烦

void update();
<update id="update" parameterType="user">
    UPDATE user
    <!--
    set标签的作用:
    1. 可以代替update 表名 set ...的SQL语句中的set关键字,使用set标签会自动加上去
    2. 会自动去除最后一个条件的逗号
    -->
    <set>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="birthday != null">
            birthday = #{birthday},
        </if>
        <if test="sex != null">
            sex = #{sex},
        </if>
        <if test="address != null">
            address = #{address},
        </if>
    </set>
    WHERE id = #{id}
</update>
public void test() {
    User select = new User();
    select.setId(10);
    select.setUsername("测试名字");
    insert.setBirthday(new Date());
    insert.setSex("man");
    insert.setAddress("广东肇庆");
    userMapper.update(insert);
}

2.3 foreach标签

常用场景是对集合进行遍历

<foreach>标签,它的属性:

  • collection:代表要遍历的集合元素
  • open:代表语句的开始部分
  • close:代表结束部分
  • item:代表遍历集合的每个元素,生成的变量名
  • sperator:代表分隔符

2.3.1 集合

List<User> findByList(List<Integer> ids);
<select id="findByList" resultType="User" parameterType="list">
    select * from `user`
    <where>
        <foreach collection="list" open="id in (" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>
public void test() {
    List<Integer> ids = new ArrayList<>(); 
    ids.add(46); 
    ids.add(48); 
    ids.add(51);
    userMapper.findByList(ids);
}

2.3.2 数组

List<User> findByArray(List<Integer> ids);
<select id="findByArray" resultType="User" parameterType="int">
    select * from `user`
    <where>
        <foreach collection="array" open="id in (" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>
public void test() {
    Integer[] ids = {46, 48, 51};
    userMapper.findByArray(ids);
}

如果是普通数组,collection属性值为array

2.4 SQL片段

主要是将多个标签出现重复的SQL语句进行抽取出来,然后在使用到的地方通过 include 引用,从而达到重复使用的效果

<!--抽取的sql片段--> 
<sql id="selectUser"> 
    SELECT * FROM `user` 
</sql>

<select id="findByList" resultType="User" parameterType="list">
    <include refid="selectUser">
    <where>
        <foreach collection="list" open="id in (" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>

</select>

3. 核心配置文件深入

3.1 plugins标签

通过plugins标签可以使用第三方的插件进行功能的扩展,例如pageHelper是将分页的复杂操作进行封装

使用步骤:

  • 导入PageHelper的相关依赖
<!-- 分页助手 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>3.7.5</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>0.9.1</version>
</dependency>
  • 在MyBatis核心配置文件中配置PageHelper插件
<!-- 分页助手的插件 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
    <!-- 指定方言 -->
    <property name="dialect" value="mysql"/>
</plugin>
  • 测试
@Test 
public void testPageHelper() { 
    //设置分页参数 
    PageHelper.startPage(1,2); 
    List<User> select = userMapper.select(null); 
    for(User user : select){ 
        System.out.println(user); 
        } 
    }

4. Mybatis多表查询

数据准备

CREATE DATABASE `mybatis_db`;
USE `mybatis_db`;
CREATE TABLE `user` (  
`id` int(11) NOT NULL auto_increment,  
`username` varchar(32) NOT NULL COMMENT '用户名称',  
`birthday` datetime default NULL COMMENT '生日',  
`sex` char(1) default NULL COMMENT '性别',  
`address` varchar(256) default NULL COMMENT '地址',
 PRIMARY KEY  (`id`)) 
 ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- insert....
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'子慕','2020-11-11 00:00:00','男','北京海淀'),(2,'应颠','2020-12-12 00:00:00','男','北京海淀');

DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (  
`id` INT(11) NOT NULL AUTO_INCREMENT, 
`ordertime` VARCHAR(255) DEFAULT NULL,  
`total` DOUBLE DEFAULT NULL,  
`uid` INT(11) DEFAULT NULL,  
PRIMARY KEY (`id`),  
KEY `uid` (`uid`),  
CONSTRAINT `orders_ibfk_1` 
FOREIGN KEY (`uid`) 
REFERENCES `user` (`id`)) 
ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ------------------------------ Records of orders-- ----------------------------
INSERT INTO `orders` VALUES ('1', '2020-12-12', '3000', '1');
INSERT INTO `orders` VALUES ('2', '2020-12-12', '4000', '1');
INSERT INTO `orders` VALUES ('3', '2020-12-12', '5000', '2');
-- ------------------------------ Table structure for sys_role-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (  
`id` INT(11) NOT NULL AUTO_INCREMENT, 
`rolename` VARCHAR(255) DEFAULT NULL,  
`roleDesc` VARCHAR(255) DEFAULT NULL,  
PRIMARY KEY (`id`)) 
ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ------------------------------ Records of sys_role-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'CTO', 'CTO');
INSERT INTO `sys_role` VALUES ('2', 'CEO', 'CEO');
------------------------------ Table structure for sys_user_role-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (  
`userid` INT(11) NOT NULL,  
`roleid` INT(11) NOT NULL,  
PRIMARY KEY (`userid`,`roleid`),  
KEY `roleid` (`roleid`),  
CONSTRAINT `sys_user_role_ibfk_1` 
FOREIGN KEY (`userid`) 
REFERENCES `sys_role`(`id`),  
CONSTRAINT `sys_user_role_ibfk_2` 
FOREIGN KEY (`roleid`) REFERENCES `user`(`id`)) 
ENGINE=INNODB DEFAULT CHARSET=utf8;
-- ------------------------------ Records of sys_user_role-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1');
INSERT INTO `sys_user_role` VALUES ('2', '1');
INSERT INTO `sys_user_role` VALUES ('1', '2');
INSERT INTO `sys_user_role` VALUES ('2', '2');
SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;

4.1 一对一查询

需求:查询指定订单号的订单和对应的用户信息

SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;

4.1.1 代码实现

Order实体类

package com.fuyi.entity;

import java.util.Date;

/**
 * @author raofy
 * @date 2021-05-11 17:34
 * @desc
 */
public class Order {

    private Integer id;

    private Date ordertime;

    private double money;

    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", ordertime=" + ordertime +
                ", money=" + money +
                ", user=" + user +
                '}';
    }
}

编写Mapper接口

package com.fuyi.mapper;

import com.fuyi.entity.Order;

import java.util.List;

/**
 * @author raofy
 * @date 2021-05-11 17:36
 * @desc
 */
public interface OrderMapper {

    /**
     * 查询所有订单和对应的用户信息(一对一,多对一)
     * @return
     */
    List<Order> findAllWithUser();
}

编写Mapper.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.fuyi.mapper.OrderMapper">
    <resultMap id="baseResultMap" type="com.fuyi.entity.Order">
        <id property="id" column="id"></id>
        <result property="money" column="total"></result>
        <result property="ordertime" column="ordertime"></result>
        <!--
        一对多,多对一,使用association进行相关联
        property="username" Java实体类的属性名
        column="username"   表中字段名
        关联条件id = uid
        -->
        <association property="user" javaType="com.fuyi.entity.User">
            <id property="id" column="uid"></id>
            <result property="username" column="username"></result>
            <result property="birthday" column="birthday"></result>
            <result property="sex" column="sex"></result>
            <result property="address" column="address"></result>
        </association>
    </resultMap>

    <select id="findAllWithUser" resultMap="baseResultMap">
        SELECT * FROM orders o left join user u on o.uid = u.id
    </select>
</mapper>

配置sqlMapConfig.xml文件

<?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>
    <!--环境配置-->
    <environments default="development">
        <!--使用MySQL环境-->
        <environment id="development">
            <!--使用JDBC类型事务管理器-->
            <transactionManager type="JDBC"></transactionManager>
            <!--使用连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <property name="url" value="jdbc:mysql:///mybatis_db?useUnicode=true&characterEncoding=UTF-8"></property>
                <property name="username" value="root"></property>
                <property name="password" value="123456"></property>
            </dataSource>
        </environment>
    </environments>
    <!--加载映射配置-->
    <mappers>
        <!--批量加载-->
        <package name="com.fuyi.mapper"/>
    </mappers>
</configuration>

编写测试类

package com.fuyi.test;

import com.fuyi.entity.Order;
import com.fuyi.mapper.OrderMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author raofy
 * @date 2021-05-11 17:49
 * @desc
 */
public class OrderTest {

    /**
     * 一对一,多对一测试例子
     */
    @Test
    public void oneToOneTest() throws IOException {
        // 1. 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

        // 2. 获取SQLFactory工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 3. 获取SQLSession对象
        SqlSession sqlSession = factory.openSession();

        // 4. 获取mapper
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

        // 5. 执行查询
        List<Order> result = mapper.findAllWithUser();

        // 6. 打印结果
        result.forEach(System.out::println);

        sqlSession.close();

        resourceAsStream.close();
    }
}

总结

  • 编写实体类
  • 编写对应的mapper接口
  • 编写对应的SQL语句和映射结果
  • 测试

4.2 一对多查询

查询所有用户的对应的用户订单表

4.2.1 编写实体类

package com.fuyi.entity;

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

/**
 * @author raofy
 */
public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<Order> orderList;
  
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", orderList=" + orderList +
                '}';
    }
}

4.2.2 编写mapper

List<User> findAllWithOrder();

4.2.3 编写mapper.xml文件

   <resultMap id="baseResultMap" type="com.fuyi.entity.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <!--
        一对多使用collection             标签进行关联
        property="orderList"           封装到集合的属性名
        ofType="com.fuyi.entity.Order" 封装集合的泛型类型
        -->
        <collection property="orderList" ofType="com.fuyi.entity.Order">
            <id property="id" column="oid"></id>
            <result property="ordertime" column="ordertime"></result>
            <result property="money" column="total"></result>
        </collection>
    </resultMap>

    <select id="findAllWithOrder" resultMap="baseResultMap">
        select *, o.id oid from user u left join orders o on u.id = o.uid;
    </select>

4.2.4 测试

    @Test
    public void getUsersTest() throws IOException {
        // 1. 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

        // 2. 获取SQLFactory工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 3. 获取SQLSession对象
        SqlSession sqlSession = factory.openSession();

        // 4. 执行SQL参数
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        // 5. 查询
        List<User> result = mapper.findAllWithOrder();

        // 6. 打印
        result.forEach(System.out::println);

        // 7. 释放资源
        sqlSession.close();
    }

4.3 多对多查询

查询所用用户和对应的角色身份

4.3.1 编写实体类


package com.fuyi.entity;

/**
 * @author raofy
 * @version 1.0.0
 * @createTime 2021-05-13 23:21
 * @Description TODO
 */
public class SysRole {
    private Integer id;
    private String roleName;
    private String roleDesc;

    @Override
    public String toString() {
        return "SysRole{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }
}

package com.fuyi.entity;

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

/**
 * @author raofy
 */
public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<SysRole> roleList;
    private List<Order> orderList;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", roleList=" + roleList +
                ", orderList=" + orderList +
                '}';
    }
}

4.3.2 编写mapper

List<User> findAllWithRole();

4.3.3 编写mapper.xml

   <resultMap id="roleBaseResultMap" type="com.fuyi.entity.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <collection property="roleList" ofType="com.fuyi.entity.SysRole">
            <id property="id" column="rid"></id>
            <result property="roleName" column="roleName"></result>
            <result property="roleDesc" column="roleDesc"></result>
        </collection>
    </resultMap>

    <select id="findAllWithRole" resultMap="roleBaseResultMap">
        select u.*, sr.id rid, sr.rolename, sr.roleDesc
        from user u
        left join sys_user_role sur
        on u.id = sur.userid
        left join sys_role sr
        on sur.roleid = sr.id
    </select>

4.3.4 测试

   /**
     * 多对多关联查询
     *
     * @throws IOException
     */
    @Test
    public void manyToManyTest() throws IOException {
        // 1. 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

        // 2. 获取SQLFactory工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 3. 获取SQLSession对象
        SqlSession sqlSession = factory.openSession();

        // 4. 执行SQL参数
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        // 5. 查询
        List<User> result = mapper.findAllWithRole();

        // 6. 打印
        result.forEach(System.out::println);

        // 7. 释放资源
        sqlSession.close();
    }

4.4 本章总结

在单表或者多表查询种

  • 多对一(一对一)配置:使用resultMap和association标签做映射配置
  • 一对多配置:使用resultMap和collection
  • 多对多配置:使用resultMap和collection
  • 多对多和一对多很相似,难度在于SQL语句的编写

5. Mybatis嵌套查询

什么是嵌套查询,就是原本是多表查询中的联合语句拆分成各自的单表查询,再将他们嵌套起来进行查询

5.1 一对一

需求查询一个订单并查询出该订单的用户信息

5.1.1 SQL语句

# 查询出指定id的订单
select * from orders

# 通过上述查询出的uid外键去查询出用户信息
select * from user where id = uid

5.1.2 编写mapper

/**
  * 一对一嵌套查询
  * @return
  */
List<Order> findAllAndUser();

5.1.3 Mapper.xml映射文件

  • OrderMapper.xml
    <resultMap id="nestedQueryResultMap" type="com.fuyi.entity.Order">
        <id property="id" column="id"></id>
        <result property="money" column="total"></result>
        <result property="ordertime" column="ordertime"></result>
        <association property="user" javaType="com.fuyi.entity.User" select="com.fuyi.mapper.UserMapper.findById" column="uid"></association>
    </resultMap>
    <select id="findAllAndUser" resultMap="nestedQueryResultMap">
        select * from orders
    </select>
  • UserMapper.xml
    <select id="findById" resultType="com.fuyi.entity.User" parameterType="integer">
        select * from user where id = #{id}
    </select>

5.1.4 测试类

   /**
     * 一对一,多对一嵌套查询测试例子
     */
    @Test
    public void oneToOneNestedQueryTest() throws IOException {
        // 1. 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

        // 2. 获取SQLFactory工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 3. 获取SQLSession对象
        SqlSession sqlSession = factory.openSession();

        // 4. 获取mapper
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

        // 5. 执行查询
        List<Order> result = mapper.findAllAndUser();

        // 6. 打印结果
        result.forEach(System.out::println);

        sqlSession.close();

        resourceAsStream.close();
    }

5.1.5 总结

所谓的嵌套查询,就是使用association标签进行调用其他的sql语句,相比于多表查询,将问题拆成多个小问题去解决,从而简化了多表查询操作

5.2 一对多

需求查询所有用户及订单信息

5.2.1 SQL语句

# 查询所有用户
select * from user
# 通过用户的id查询所有订单信息
select * from order where uid = id

5.2.2 编写mapper

   /**
     * 一对多嵌套查询
     *
     * @return
     */
    List<User> findAllAndOrder();

5.2.3 编写mapper.xml映射文件

  • UserMapper.xml
    <resultMap id="nestedQueryResultMap" type="com.fuyi.entity.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <collection property="orderList" ofType="com.fuyi.entity.Order" column="id" select="com.fuyi.mapper.OrderMapper.findByUid"></collection>
    </resultMap>
    <select id="findAllAndOrder" resultMap="nestedQueryResultMap">
        select * from user
    </select>
  • OrderMapper.xml
    <select id="findByUid" resultType="com.fuyi.entity.Order">
        select * from orders where uid = #{id};
    </select>

5.2.4 测试

   /**
     * 一对多嵌套查询
     *
     * @throws IOException
     */
    @Test
    public void oneToManyNestedQueryTest() throws IOException {
        // 1. 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

        // 2. 获取SQLFactory工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 3. 获取SQLSession对象
        SqlSession sqlSession = factory.openSession();

        // 4. 执行SQL参数
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        // 5. 查询
        List<User> result = mapper.findAllAndOrder();

        // 6. 打印
        result.forEach(System.out::println);

        // 7. 释放资源
        sqlSession.close();
    }

5.3 多对多

需求:查询用户对应的角色身份

5.3.1 SQL语句

# 查询所有用户
select * from user
# 根据用户id查询用户的角色列表
select * from sys_role sr left join sys_user_role sur on sur.roleid = sr.id where sur.userid = id

5.3.2 编写实体类

package com.fuyi.entity;

/**
 * @author raofy
 * @version 1.0.0
 * @createTime 2021-05-13 23:21
 */
public class SysRole {
    private Integer id;
    private String roleName;
    private String roleDesc;

    @Override
    public String toString() {
        return "SysRole{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }
}

5.3.2 编写mapper

public interface UserMapper {
    /**
     * 多对多嵌套查询
     *
     * @return
     */
    List<User> findAllAndRole();
}

5.3.3 编写mapper.xml映射文件

  • UserMapper.xml
    <resultMap id="nestedQueryRoleResultMap" type="com.fuyi.entity.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <collection property="roleList" ofType="com.fuyi.entity.SysRole" column="id" select="com.fuyi.mapper.SysRoleMapper.findByUid"></collection>
    </resultMap>
    <select id="findAllAndRole" resultMap="nestedQueryRoleResultMap">
        select * from user
    </select>
  • SysRoleMapper.xml
    <select id="findByUid" resultType="com.fuyi.entity.SysRole">
        select * from sys_role sr left join sys_user_role sur on sur.roleid = sr.id where sur.userid = #{id}
    </select>

5.3.4 测试

   /**
     * 多对多嵌套查询
     *
     * @throws IOException
     */
    @Test
    public void manyToManyNestedQueryTest() throws IOException {
        // 1. 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

        // 2. 获取SQLFactory工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 3. 获取SQLSession对象
        SqlSession sqlSession = factory.openSession();

        // 4. 执行SQL参数
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        // 5. 查询
        List<User> result = mapper.findAllAndRole();

        // 6. 打印
        result.forEach(System.out::println);

        // 7. 释放资源
        sqlSession.close();
    }

5.3.5 总结

  • 一对一配置:使用resultMap + association,通过column条件,执行select查询
  • 一对多配置:使用resultMap + collection,通过column条件,执行select查询
  • 多对多配置:使用resultMap + collection,通过column条件,执行select查询
  • 优点:简化多表查询操作
  • 缺点:执行多次sql语句,浪费数据库性
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容