1.1 前言
实际开发过程中有时候我们并不需要在加载用户信息时,就加载他的账户信息。 而是在使用用户账号的时候,再向数据库查询,此时就是我们所说的延迟加载。
1.2 何为延时加载
延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
好处:先从单表查询,需要时再从关联表去查询,大大提高数据库性能,因为单表要比关联查询多张表速度要快。
坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
如何实现延迟加载:前面实现多表操作时,我们使用了 resultMap 来实现一对一,一对多,多对多关系的操作。主要是通过 association、 collection 实现一对一及一对多映射。 association、 collection 具备延迟加载功能。
1.3 什么是及时加载,什么是延时加载
- 及时加载:一次加载所有的数据。
- 延时加载:也叫做懒加载,在需要用到数据的时候查询数据。
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 开启延迟加载支持(总结):二种方式
方式一:配置延迟加载的全局开关
方式二:fetchType属性
通过在association标签中配置fetchType属性为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();
}
}