Mybatis的延迟加载

Mybatis的延迟加载

 一、什么是延迟加载

延迟加载:
  就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
好处:
  先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。 坏处:   因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

二、实现需求

  

需求:
  查询账户(Account)信息并且关联查询用户(User)信息。如果先查询账户(Account)信息即可满足要求,当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。
  association、collection 具备延迟加载功能。

三、使用association实现延迟加载  

需求:
查询账户信息同时查询用户信息。

3.1 账户的持久层dao接口

package com.llb.dao;

import com.llb.domain.Account;

import java.util.List;

/**
 * Ceate By llb on 2019/8/7
 */
public interface AccountMapper {
    
    /**
     * 查询账户所对应的的用户
     */
    List<Account> findAccountAndUser();

}

3.2 账户的持久层映射文件

<?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.llb.dao.AccountMapper">
    <!--定义封装account和user的resultMap-->
    <resultMap id="accountMap" type="com.llb.domain.Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--一对一的关系映射    JavaType:对应的是哪个类
            select 指定的内容:查询用户的唯一标识
        -->
        <association property="user" column="uid" javaType="com.llb.domain.User" select="com.llb.dao.UserMapper.findById" ></association>
    </resultMap>

    <select id="findAccountAndUser" resultMap="accountMap">
        select * from account
    </select>
</mapper>

3.3 用户的持久层接口

package com.llb.dao;

import com.llb.domain.User;

import java.util.List;

/**
 * 用户的持久层接口
 * Ceate By llb on 2019/8/5
 */
public interface UserMapper {
    
    /**
     * 根据id查询所有用户
     * @return
     */
    User findById(Integer id);

    /**
     * 查询出所有用户,包含账户信息
     * @return
     */
    List<User> findAccountAndUser();
}

3.4 用户的持久层映射文件

<?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.llb.dao.UserMapper">

    <!--配置 查询结果的列名和实体类的属性名的对应关系-->
    <resultMap id="userMap" type="com.llb.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!--配置user对象中accounts集合的映射      ofType:表示集合的类型-->
        <collection property="accounts" ofType="com.llb.domain.Account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>

    <!--查询出用户所对应的账户-->
    <select id="findAccountAndUser" resultMap="userMap">
        select * from user left outer join account on user.id = account.uid
    </select>

    <!--根据id查询用户-->
    <select id="findById" resultType="user" parameterType="int">
        select * from user where id = #{id}
    </select>

</mapper>

3.5 开启mybatis的延迟加载策略

进入 Mybaits 的官方文档,找到 settings 的说明信息:
 
我们需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置。
    <!--配置参数,延迟加载-->
    <settings>
        <!--开启mybatis支持延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--不配置也可以:默认为false;允许触发方法进行延迟加载,否则立即加载-->
        <setting name="aggressiveLazyLoading" value="false"></setting>
    </settings>

3.6 编写测试只查账户信息不查用户信息

 
package com.llb.test;

import com.llb.dao.AccountMapper;
import com.llb.dao.UserMapper;
import com.llb.domain.Account;
import com.llb.domain.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.List;

/**
 * Ceate By llb on 2019/8/5
 */
public class AccountTest {

    InputStream in = null;
    AccountMapper mapper = null;
    SqlSession sqlSession = null;

    /**
     * 在测试方法执行前执行
     * @throws IOException
     */
    @Before
    public void init() throws IOException {
        //1.读取配置文件,生成字节流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取sqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取sqlSession对象
        sqlSession = factory.openSession();
        //4.获取dao的代理对象
        mapper = sqlSession.getMapper(AccountMapper.class);
    }

    /**
     * 测试方法执行后执行
     * @throws IOException
     */
    @After
    public void destory() throws IOException {
        sqlSession.commit();
        //6.释放资源
        sqlSession.close();
        in.close();
    }


    /**
     * 查询出账户所对应的user
     */
    @Test
    public void testFindAccountAndUser(){
        List<Account> accountUser = mapper.findAccountAndUser();
//        for (Account account: accountUser) {
//            System.out.println(account);
//        }
    }


}

 

3.7 测试结果

当不进行配置时,立即加载,查询account所对应的user,一起将user查询出来:

  

  配置后,对account进行查询放入到list集合中,并没有涉及到user对象,所以就没有发出 SQL 语句查询账户所关联的 User 对象的查询。:

  

四、使用collection实现延迟加载

同样我们也可以在一对多关系配置的<collection>结点中配置延迟加载策略。
 <collection>结点中也有 select 属性,column 属性。
  需求:
    完成加载用户对象时,查询该用户所拥有的账户信息。

4.1 在User实体类中加入List<Account>属性

package com.llb.domain;

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

/**
 * Ceate By llb on 2019/8/5
 */
public class User implements Serializable{

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

    //一对多关系。一的方包含多的一方所有对象
    private List<Account> accounts;

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

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getAddress() {
        return address;
    }

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

    public String getSex() {
        return sex;
    }

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

    public Date getBirthday() {
        return birthday;
    }

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

4.2 编写用户持久层接口的方法和配置文件

UserMapper.class:

package com.llb.dao;

import com.llb.domain.User;

import java.util.List;

/**
 * 用户的持久层接口
 * Ceate By llb on 2019/8/5
 */
public interface UserMapper {

    /**
     * 根据id查询所有用户
     * @return
     */
    User findById(Integer id);

    /**
     * 查询出所有用户,包含账户信息
     * @return
     */
    List<User> findAccountAndUser();
}

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.llb.dao.UserMapper">

    <!--配置 查询结果的列名和实体类的属性名的对应关系-->
    <resultMap id="userMap" type="com.llb.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!--配置user对象中accounts集合的映射      ofType:表示集合的类型-->
        <collection property="accounts" ofType="com.llb.domain.Account" select="com.llb.dao.AccountMapper.findAccountById" column="id"></collection>
    </resultMap>

    <!--查询出用户所对应的账户-->
    <select id="findAccountAndUser" resultMap="userMap">
        select * from user
    </select>

    <!--根据id查询用户-->
    <select id="findById" resultType="user" parameterType="int">
        select * from user where id = #{id}
    </select>

</mapper>

4.3 使用测试方法进行测试

package com.llb.test;

import com.llb.dao.AccountMapper;
import com.llb.dao.UserMapper;
import com.llb.domain.Account;
import com.llb.domain.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.List;

/**
 * Ceate By llb on 2019/8/5
 */
public class UserTest {

    InputStream in = null;
    UserMapper mapper = null;
    SqlSession sqlSession = null;

    /**
     * 在测试方法执行前执行
     * @throws IOException
     */
    @Before
    public void init() throws IOException {
        //1.读取配置文件,生成字节流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取sqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取sqlSession对象
        sqlSession = factory.openSession();
        //4.获取dao的代理对象
        mapper = sqlSession.getMapper(UserMapper.class);
    }

    /**
     * 测试方法执行后执行
     * @throws IOException
     */
    @After
    public void destory() throws IOException {
        sqlSession.commit();
        //6.释放资源
        sqlSession.close();
        in.close();
    }

    /**
     * 查询出所有用户所对应的账户
     */
    @Test
    public void findUserAndAccount(){
        List<User> users = mapper.findAccountAndUser();
     
    }

}

测试结果:

  

  我们没有使用Accout,也只对User进行了查询。

 

Mybatis缓存

待更新。。。

源码:https://github.com/PopsiCola/SSM-mybatis/tree/association_lazy

欢迎star

 

posted @ 2019-08-08 19:33  PopsiCola  阅读(291)  评论(0编辑  收藏  举报