1.1 前言

  • 实际开发过程中有时候我们并不需要在加载用户信息时,就加载他的账户信息。 而是在使用用户账号的时候,再向数据库查询,此时就是我们所说的延迟加载

1.2 何为延时加载 

  • 延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.

  • 好处:先从单表查询,需要时再从关联表去查询,大大提高数据库性能,因为单表要比关联查询多张表速度要快。

  • 坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

  • 如何实现延迟加载:前面实现多表操作时,我们使用了 resultMap 来实现一对一,一对多,多对多关系的操作。主要是通过 association、 collection 实现一对一及一对多映射。 association、 collection 具备延迟加载功能

 1.3 什么是及时加载,什么是延时加载

  1. 及时加载:一次加载所有的数据。
  2. 延时加载:也叫做懒加载,在需要用到数据的时候查询数据。

Mybatis 延迟加载Lazy策略

1.4 一对一实现延时加载 

  • 需求:Account账户与User用户就是一对一关系。
  • 现在要实现一对一的延迟加载,查询账户时候,只查询账户;再使用账户对用的用户信息时候,才查询用户.
  • 数据中实现刚才的需求描述:
  • --  查询账户时候,只查询账户;再使用账户对用的用户信息时候,才查询用户.
    --  1)查询账户
    SELECT * FROM account;
    --  2)账户对应的用户
    SELECT * FROM USER WHERE id=46

1.4.1 创建项目、添加依赖 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>javaee</groupId>
    <artifactId>mybatis_lazy</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <!--导入依赖-->
    <dependencies>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <!--test表示只在测试类中有效-->
            <scope>test</scope>
        </dependency>

        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.4</version>
        </dependency>

        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>

        <!--log4j,打印日志和日志文件,mybatis需要这个jar-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>com.itheima</groupId>
            <artifactId>mybatis02_01_crud</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>
    
</project>

1.4.2 主配置文件、实体类

package com.sunny.entity;

import java.io.Serializable;

/**
 * 账户实体类
 */
public class Account implements Serializable {
    private int accountId;
    private int uid;
    private double money;

    /**
     * 多表查询封装User数据
     */
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Account() {
    }

    public Account(int accountId, int uid, double money) {
        this.accountId = accountId;
        this.uid = uid;
        this.money = money;
    }

    public int getAccountId() {
        return accountId;
    }

    public void setAccountId(int accountId) {
        this.accountId = accountId;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "accountId=" + accountId +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

 

package com.sunny.entity;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
    private int id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public User() {
    }

    public User(int id, String username, Date birthday, String sex, String address) {
        this.id = id;
        this.username = username;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
  • IAccountDao接口  查询全部 

package com.sunny.dao;

import com.sunny.entity.Account;

import java.util.List;

public interface IAccountDao {

    //查找全部
    public abstract List<Account> findAll();
}
  • 要求查询账户使用延迟加载,再使用账户的用户信息时候,才查询用户。

        现在,需要写一个根据用户id查询用户的方法:

  •  IUserDao接口 根据用户id查询(延迟加载要用到)

package com.sunny.dao;

import com.sunny.entity.QueryVO;
import com.sunny.entity.User;

import java.util.List;


/**
 * 用户数据访问接口
 */
public interface IUserDao {
    //根据用户id查询
    User findById(int id);
}

1.4.3 IUserDao.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">
<!--namespace名称空间,用于定义是哪个类的映射文件,这里需要写所映射接口的类全名-->
<mapper namespace="com.sunny.dao.IUserDao">
    <select id="findById" resultType="user" parameterType="int">
        SELECT * FROM USER WHERE id = #{id}
    </select>
</mapper>

1.4.4 IAccountDao.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">
        <!--namespace对应的dao接口路径-->
<mapper namespace="com.sunny.dao.IAccountDao">

    <!--一对一:延迟加载查询 结果集封装、以及延迟加载配置-->
    <resultMap id="accountResultMap" type="account">
        <id property="accountId" column="accountId"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--association:一对一:延迟加载
            一对一配置:
            property:对用account对象的user属性
            javaType:是user属性的类型(com.sunny.entity.User)
            column:account表的外键字段,也会作为延迟加载select调用的方法参数自动传入

            延迟加载配置:select 延迟加载查询配置
                        根据账户中用户的id,查询用户信息
                        所以需要找到查询方法:com.sunny.dao.IUserDao.findById
                        fetchType="lazy" 配置开启延迟加载(覆盖全局的配置)
        -->
        <association property="user" javaType="user" column="uid"
                     select="com.sunny.dao.IUserDao.findById"
                     fetchType="lazy"></association>
    </resultMap>

    <!--一对一:延迟加载查询-->
    <select id="findAll" resultMap="accountResultMap">
        SELECT * FROM account;
    </select>

</mapper>

1.4.5 测试

import com.sunny.dao.IAccountDao;
import com.sunny.dao.IUserDao;
import com.sunny.entity.Account;
import com.sunny.entity.QueryVO;
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.After;
import org.junit.Before;
import org.junit.Test;

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

public class AccountDaoTest {
    InputStream in = null;
    SqlSession sqlSession = null;
    IAccountDao accountDao = null;
    @Before
    public void before() throws IOException {
        //获取主配置文件的输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSession工厂构造类
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //根据SQLSession工厂构造类创建SqlSession工厂
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        //根据sqlSession工厂创建SqlSession
        sqlSession =  sqlSessionFactory.openSession();
        accountDao = sqlSession.getMapper(IAccountDao.class);
    }

    @Test
    public void find(){
        List<Account> accountList = accountDao.findAll();
        for (Account a : accountList) {
            System.out.println(a.getUid()+","+a.getMoney());
            //使用账户下的用户信息,才向数据库发送查询SQL,这个叫延迟加载
            System.out.println(a.getUser().getUsername());
        }
    }

    @After
    public void after() throws IOException {
        //手动提交事务
        sqlSession.commit();
        //关闭资源
        sqlSession.close();
        in.close();
    }
}

1.4.6 开启延迟加载支持(总结):二种方式

方式一:配置延迟加载的全局开关

Mybatis 延迟加载Lazy策略

Mybatis 延迟加载Lazy策略 方式二:fetchType属性

通过在association标签中配置fetchType属性为lazy开启延迟加载。

Mybatis 延迟加载Lazy策略


2.1 一对多实现延时加载

  • User用户与Account账户,是一对多的关系
  • 需求:查询用户的时候,只查询用户信息,在使用用户的账户信息的时候才向数据库发送查询语句,
  • 一对多,延迟加载,数据库查询实现。

 -- 一对多,延迟加载,数据库查询实现
-- 1)先查询用户
SELECT * FROM USER;
-- 2)再根据用户的id,查询账户
SELECT * FROM account WHERE uid=46

  •  IUserDao接口

package com.sunny.dao;

import com.sunny.entity.QueryVO;
import com.sunny.entity.User;

import java.util.List;


/**
 * 用户数据访问接口
 */
public interface IUserDao {
    //根据用户id查询
    User findById(int id);

    //查询所有用户,延迟加载(加载所有账户)
    List<User> findAll();
}
  • IAccountDao接口 

package com.sunny.dao;

import com.sunny.entity.Account;

import java.util.List;

public interface IAccountDao {

    //查找全部
    public abstract List<Account> findAll();

    //根据用户id,查询用户所有的账户信息
    List<Account> findAccountByUserId(int uid);
}
  • IAccountDao.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">
        <!--namespace对应的dao接口路径-->
<mapper namespace="com.sunny.dao.IAccountDao">

    <!--一对一:延迟加载查询(2)结果集封装、以及延迟加载配置-->
    <resultMap id="accountResultMap" type="account">
        <id property="accountId" column="accountId"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--association:一对一:延迟加载
            一对一配置:
            property:对用account对象的user属性
            javaType:是user属性的类型(com.sunny.entity.User)
            column:account表的外键字段,也会作为延迟加载select调用的方法参数自动传入

            延迟加载配置:select 延迟加载查询配置
                        根据账户中用户的id,查询用户信息
                        所以需要找到查询方法:com.sunny.dao.IUserDao.findById
                        fetchType="lazy" 配置开启延迟加载(覆盖全局的配置)
        -->
        <association property="user" javaType="user" column="uid"
                     select="com.sunny.dao.IUserDao.findById"
                     fetchType="lazy"></association>
    </resultMap>

    <!--一对一:延迟加载查询(1)-->
    <select id="findAll" resultMap="accountResultMap">
        SELECT * FROM account;
    </select>

    <!--根据用户查询账户-->
    <select id="findAccountsByUserId" parameterType="int" resultType="account">
        SELECT * FROM account WHERE uid=#{userId}
    </select>

</mapper>
  • IUserDao.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">
<!--namespace名称空间,用于定义是哪个类的映射文件,这里需要写所映射接口的类全名-->
<mapper namespace="com.sunny.dao.IUserDao">
    <select id="findById" resultType="user" parameterType="int">
        SELECT * FROM USER WHERE id = #{id}
    </select>

    <!--查询用户,使用延迟加载(2) 延迟加载配置-->
    <resultMap id="userResultMap" type="user">
        <!--a. 封装用户信息-->
        <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>
        <!--
        b. 一对多,延迟加载配置
        collection  一对多
            property 用户对象的accounts集合属性
            ofType   集合中元素的类型(com.itheima.entity.Account)
            column   用户的主键,会作为select对应的方法的参数id
            select   延迟加载配置。 根据用户查询账户,查询结果封装到List<Account> accounts;
            fetchType="lazy"  开启延迟加载
        -->
        <collection
                property="accounts" ofType="account" column="id"
                select="com.sunny.dao.IAccountDao.findAccountsByUserId"
                fetchType="lazy"></collection>
    </resultMap>

    <!--查询用户,使用延迟加载(1)-->
    <select id="findAll" resultMap="userResultMap">
        SELECT * FROM USER
    </select>

</mapper>
  •  测试

import com.sunny.dao.IUserDao;
import com.sunny.entity.QueryVO;
import com.sunny.entity.User;
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.After;
import org.junit.Before;
import org.junit.Test;

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

public class UserDaoTest {
    InputStream in = null;
    SqlSession sqlSession = null;
    IUserDao userDao = null;
    @Before
    public void before() throws IOException {
        //获取主配置文件的输入流
         in = Resources.getResourceAsStream("SqlMapConfig.xml");
         //创建SqlSession工厂构造类
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //根据SQLSession工厂构造类创建SqlSession工厂
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        //根据sqlSession工厂创建SqlSession
        sqlSession =  sqlSessionFactory.openSession();
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @Test
    public void find(){
        List<User> list = userDao.findAll();
        for (User user : list) {
            System.out.println(user.getId()+","+user.getUsername());
            //使用账户信息,才向数据库发送查询账户的SQL,说明延迟加载生效
            System.out.println(user.getAccount());

        }
    }


    @After
    public void after() throws IOException {
        //手动提交事务
        sqlSession.commit();
        //关闭资源
        sqlSession.close();
        in.close();
    }
}

 

相关文章: