侧边栏壁纸
博主头像
coydone博主等级

记录学习,分享生活的个人站点

  • 累计撰写 306 篇文章
  • 累计创建 51 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Mybatis延迟加载

coydone
2022-03-10 / 0 评论 / 0 点赞 / 329 阅读 / 6,000 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-04-10,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

问题引入

在一对多中,当我们有一个用户,它有100个账户。当我们在查询数据时,会遇到如下问题:

在查询用户的时候,要不要把关联的账户查出来?

在查询账户的时候,要不要把关联的用户查出来?

在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询的。(延迟加载)

在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来。(立即加载)

延迟加载:在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)。

立即加载:不管用不用,只要一调用方法,马上发起查询。

针对数据库表的关系来说:

  • 一对多,多对多:通常情况下我们都是采用延迟加载。

  • 多对一,一对一:通常情况下我们都是采用立即加载。

准备

搭建一对多查询的Mybatis环境。可以使用User用户表和Account账户表。(一个User可以有多个Account)

1、数据库表

User表

CREATE TABLE USER (
 `id`			INT(11)NOT NULL AUTO_INCREMENT,
 `username` 	VARCHAR(32) NOT NULL COMMENT '用户名',
 `telephone`    VARCHAR(11) NOT NULL COMMENT '手机',
 `birthday`		DATETIME DEFAULT NULL COMMENT '生日',
 `gender`  		CHAR(1) DEFAULT NULL COMMENT '性别',
 `address` 		VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

Account表

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `UID` int(11) default NULL COMMENT '用户编号',
  `MONEY` double default NULL COMMENT '金额',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、实体类

User.java

public class User implements Serializable {
    private Integer id;
    private String username;
    private String telephone;
    private Date birthday;
    private String gender;
    private String address;
    //一对多关系映射,主表实体应该包含从表实体的集合引用
    private List<Account> accounts;
	//省略getter和setter方法、toString方法
}

Account.java

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    //从表实体应该包含一个主表实体的对象引用
    private User user;
    //省略getter和setter方法、toString方法
}

3、mapper接口

public interface AccountMapper {
    //查询所有账户
    List<Account> findAll();
}

public interface UserMapper {
    // 查询所有用户信息,同时显示出该用户下的所有账户
    List<User> findAll();

    // 根据id查询用户信息
    User findById(Integer userId);
}

4、Mapper映射的xml文件

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.coydone.mapper.UserMapper">

    <!-- 定义User的resultMap-->
    <resultMap id="userAccountMap" type="User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="telephone" column="telephone"></result>
        <result property="birthday" column="birthday"></result>
        <result property="gender" column="gender"></result>
        <result property="address" column="address"></result>
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>
    
    <!-- 查询所有用户 并且显示对应账户信息 此时为一对多关系-->
    <select id="findAll" resultMap="userAccountMap">
       SELECT u.*,a.id as aid,a.uid,a.money FROM user u LEFT OUTER JOIN account a on u.id = a.uid;
    </select>
        
    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="INT" resultType="User">
        select * from user where id = #{uid}
    </select>    
</mapper>

5、测试类

我们之前一对多查询用户的方式,同时会将用户对应所有的账户信息,也查询出来。

// 测试查询所有
@Test
public void testFindAll() {
	List<User> users= userMapper.findAll();
    for (User user : users) {
        System.out.println(user);
        System.out.println(user.getAccounts());
    }
}

实现延迟加载

1、修改AccountMapper.xml

首先需要修改的就是账户的映射配置文件,可以看到我们在查询时,依旧定义了一个 resultMap 先封装了 Account ,然后通过association 进行关联 User,其中使用的就是 select 和 column 实现了延迟加载用户信息。

  • select 用来指定延迟加载所需要执行的 SQL 语句,也就是指定某个SQL映射文件中的某个select标签对的id,在这里我们指定了用户中通过id查询信息的方法。

  • column 是指关联的用户信息查询的列,在这里也就是关联的用户的主键,即id。

<mapper namespace="com.coydone.mapper.AccountMapper">
	<!-- 定义封装 Account和User 的resultMap -->
    <resultMap id="userAccountMap" type="Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 配置封装 User 的内容
            select:查询用户的唯一标识,这里要写查询方法的全限定名(包名+类名)
            column:用户根据id查询的时候,需要的参数值
        -->
        <association property="user" column="uid" javaType="User" select="com.coydone.mapper.UserMapper.findById"></association>
    </resultMap>

    <!-- 根据查询所有账户 -->
    <select id="findAll" resultMap="userAccountMap">
        SELECT * FROM account
    </select>
</mapper>

我们只执行一下账户的查询所有方法,看一下,是否能够实现我们的效果。

@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
}

可以看到,三条 SQL 语句都执行了,因为我们在测试方法之前,需要开启延迟加载功能。

2、添加延迟加载功能

官方介绍了设置延迟加载的相关信息:

如果想要开始延迟加载功能,就需要在总配置文件mybatis-config.xml 中配置 setting 属性,也就是将延迟加载 lazyLoadingEnable 的开关设置成 true ,由于是按需加载,所以还需要将积极加载修改为消极加载,也就是将 aggressiveLazyLoading 改为 false。

当然,由于我这里导入的 MyBatis 版本为 3.4.5 所以这个值默认就是 false 实际上不用设置也可以。

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
     <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

3、测试

@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
}

这一次只执行了一条查询 account 的命令,那么当用户想要查看到,每个账户对应下的用户的时候,也就是按需查询,只需要在测试时,加入对应获取方法就可以了。

@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
    for (Account account : accounts){
        System.out.println(account);
        System.out.println(account.getUser());
    }
}

总结

上面的测试,我们已经实现了延迟加载,简单的总结一下步骤:

①:执行对应的 mapper 方法,也就是上例中执行 Mapper 中 id 值为 findAll 的对应 SQL配置,只查询到账户的信息。

②:在程序中,遍历查询到的 accounts ,调用 getUser() 方法时,开始进行延迟加载。

③:进行延迟加载,调用映射文件中 id 值为 findById 的对应 SQL配置,获取到对应用户的信息。

可以看到,我们之前通过使用左外连接等的 SQL书写方式,直接就可以查询到多张表。

SELECT u.*,a.id as aid,a.uid,a.money 
	FROM user u LEFT OUTER JOIN account a on u.id = a.uid;

但是我们可以通过延迟加载,实现我们按需查询的需求,综上所述,在使用的时候,先执行简单的 SQL,然后再按照需求加载查询其他信息。

其实就是将我们原来的查询两个表的连接查询进行分解,先查询主表内容,当需要查询从表数据时,我们在根据外键查询从表中对应的数据。

0

评论区