MyBatis框架(10) —— 实现多表查询的延迟加载操作

简介

  • 通过前面的学习,我们已经掌握了Mybatis中一对一,一对多,多对多关系的配置及实现,可以实现对象的
    关联查询。实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的账户信息。此时就是我们所说的延迟加载。

延迟加载

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

目录结构

  • src
    • main
      • java
        • cn.water.dao
          • AccountDao.java(持久层接口)
          • UserDao.java(持久层接口)
        • cn.water.domain
          • Account.java(实体类)
          • User.java(实体类)
      • resources
        • cn.water.dao
          • AccountDao.xml(映射配置文件)
          • UserDao.xml(映射配置文件)
        • SqlMapConfig.xml(MyBatis主配置文件)
        • jdbcConfig.properties(数据库连接信息文件)
    • test
      • java.cn.water
        • MybatisTest.java(测试类)

MyBatis主配置文件

SqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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">


<!-- mybatis的主配置文件 -->
<configuration>

<!-- 外部配置 -->
<properties resource="jdbcConfig.properties"></properties>

<!-- 指定包:实体类-->
<typeAliases>
<package name="cn.water.domain"/>
</typeAliases>

<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>

<!-- 指定包:持久层接口 -->
<mappers>
<package name="cn.water.dao"/>
</mappers>

</configuration>

数据库连接信息文件

jdbcConfig.properties

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

实体类

Account.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package cn.water.domain;

/**
* @author Water
* @date 2019/10/11 - 8:35
* @description
*/
public class Account {

private Integer id;
private Integer uid;
private Double money;
private User user;

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

public Integer getId() {
return id;
}

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

public Integer getUid() {
return uid;
}

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

public Double getMoney() {
return money;
}

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

public User getUser() {
return user;
}

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

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package cn.water.domain;

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

/**
* @author Water
* @date 2019/10/11 - 8:35
* @description
*/
public class User {

private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", 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 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;
}

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

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

持久层接口

AccountDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.water.dao;

import cn.water.domain.Account;

import java.util.List;

/**
* @author Water
* @date 2019/10/11 - 8:35
* @description
*/
public interface AccountDao {

/* 一对一查询 */
List<Account> findAll();

/* 一对多查询 */
List<Account> findById(Integer id);

}

UserDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.water.dao;

import cn.water.domain.Account;
import cn.water.domain.User;

import java.util.List;

/**
* @author Water
* @date 2019/10/11 - 8:35
* @description
*/
public interface UserDao {

/* 一对一查询 */
User findById(Integer id);

/* 一对多查询 */
List<User> findAll();

}

映射配置文件

AccountDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?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="cn.water.dao.AccountDao">
<!-- 【次表:Account】 -->
<resultMap id="o2o" type="account">
<id column="id" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 【主表:User】 -->
<association
property="user"
javaType="user"
select="cn.water.dao.UserDao.findById"
column="uid"
>
</association>
</resultMap>
<select id="findAll" resultMap="o2o">
SELECT * FROM account
</select>

<select id="findById" parameterType="INT" resultType="account">
SELECT * FROM account WHERE uid = #{id}
</select>

</mapper>

UserDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?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="cn.water.dao.UserDao">
<select id="findById" parameterType="INT" resultType="user">
SELECT * FROM user WHERE id = #{uid}
</select>

<!-- 【主表:User】 -->
<resultMap id="o2m" type="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 【次表:Account】 -->
<collection
property="accounts"
ofType="account"
select="cn.water.dao.AccountDao.findById"
column="id"
>
</collection>
</resultMap>
<select id="findAll" resultMap="o2m">
SELECT * FROM user
</select>

</mapper>

测试类

MyBatisTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package cn.water;

import cn.water.dao.AccountDao;
import cn.water.dao.UserDao;
import cn.water.domain.Account;
import cn.water.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;

/**
* @author Water
* @date 2019/10/11 - 8:49
* @description
*/
public class MyBatisTest {


/* 成员变量 */
private InputStream inputStream;
private SqlSession session;

/* 初始化操作 */
@Before
public void init() throws IOException {
/* 加载 MyBatis配置文件 */
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
/* 获取 工厂类 */
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
/* 获取 产品类 */
session = factory.openSession(true);/* 设置自动提交 */
}

/* 销毁操作 */
@After
public void destroy() throws IOException {
session.close();
inputStream.close();
}

/* OneToOne */
@Test
public void test01(){
AccountDao dao = session.getMapper(AccountDao.class);
for (Account account : dao.findAll()) {
System.out.println(account);
}
}

/* OneToMany */
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
for (User user : dao.findAll()) {
System.out.println(user);
}
}

}

一对一查询(延迟)

  • 一对一查询:从查询 账户信息(Account) 出发,关联查询 用户信息(User)
    • 立即加载:一次多表查询
      • SELECT u.*,a.* FROM user u,account a WHERE u.id = a.uid;
    • 延迟加载:多次单表查询(提供关联信息)
      • SELECT * FROM account;(获取uid)
      • SELECT * FROM user WHERE id = #{uid}; (提供uid)

Account 实体类

  • 将 用户类(User)设置为变量
1
2
3
4
5
6
7
8
9
10
11
12
    public class Account implements Serializable {

/* 成员变量 */
private Integer id;
private Integer uid;
private Double money;
private User user; /* 用户类 */

/* toString方法 省略 */
/* Setter Getter 省略 */

}

User 实体类

1
2
3
4
5
6
7
8
9
10
11
12
public class User implements Serializable {

/* 成员变量 */
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

/* toString方法 省略 */
/* Setter Getter 省略 */
}

Account 持久层接口

1
2
/* 一对一查询 */
List<Account> findAll();

User 持久层接口

  • 返回值 对应 Account类的User变量
1
2
/* 一对一查询 */
User findById(Integer id);

Account 映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 【次表:Account】 -->
<resultMap id="o2o" type="account">
<id column="id" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 【主表:User】 -->
<association
property="user"
javaType="user"
select="cn.water.dao.UserDao.findById"
column="uid"
>
</association>
</resultMap>
<!-- 单表查询 -->
<select id="findAll" resultMap="o2o">
SELECT * FROM account
</select>

User 映射配置文件

1
2
3
4
<!-- 单表查询 -->
<select id="findById" parameterType="INT" resultType="user">
SELECT * FROM user WHERE id = #{uid}
</select>

测试类

1
2
3
4
5
6
7
@Test
public void test01(){
AccountDao dao = session.getMapper(AccountDao.class);
for (Account account : dao.findAll()) {
System.out.println(account);
}
}

运行结果

![](10.延迟加载\延迟加载 一对一查询 控制台输出.png)

一对多查询(延迟)

  • 一对多查询:从查询 用户信息(User) 出发,关联查询 账户信息(Account)
    • 立即加载:一次多表查询
      • SELECT * FROM user u LEFT OUTER JOIN account a ON u.id = a.uid;
    • 延迟加载:多次单表查询(通过关联信息)
      • SELECT * FROM user;(获取id)
      • SELECT * FROM account WHERE uid = #{id}(提供id)

User 实体类

  • 将 账户类(Account) 的List集合 设置为变量
1
2
3
4
5
6
7
8
9
10
11
12
13
public class User implements Serializable {

/* 成员变量 */
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;/* 账户类的集合 */

/* toString方法 省略 */
/* Setter Getter 省略 */
}

Account 实体类

1
2
3
4
5
6
7
8
9
10
11
public class Account implements Serializable {

/* 成员变量 */
private Integer id;
private Integer uid;
private Double money;

/* toString方法 省略 */
/* Setter Getter 省略 */

}

User 持久层接口

1
2
/* 一对多查询 */
List<User> findAll();

Account 持久层接口

  • 返回值 对应 User类的List<Account>变量
1
2
/* 一对多查询 */
List<Account> findById(Integer id);

User 映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 【主表:User】 -->
<resultMap id="o2m" type="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 【次表:Account】 -->
<collection
property="accounts"
ofType="account"
select="cn.water.dao.AccountDao.findById"
column="id"
>
</collection>
</resultMap>
<!-- 单表查询 -->
<select id="findAll" resultMap="o2m">
SELECT * FROM user
</select>

Account 映射配置文件

1
2
3
4
<!-- 单表查询 -->
<select id="findById" parameterType="INT" resultType="account">
SELECT * FROM account WHERE uid = #{id}
</select>

测试类

1
2
3
4
5
6
7
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
for (User user : dao.findAll()) {
System.out.println(user);
}
}

运行结果

![](10.延迟加载\延迟加载 一对多查询 控制台输出.png)

-------------本文结束-------------
Donate comment here