天天看点

mybatis 详解(八)------ 懒加载

本章我们讲如何通过懒加载来提高mybatis的查询效率。

  本章所有代码:http://pan.baidu.com/s/1o8p2Drs 密码:trd6

1、需求:查询订单信息,有时候需要关联查出用户信息。

  第一种方法:我们直接关联查询出所有订单和用户的信息

1

select * from orders o ,user u where o.user_id = u.id;

  分析:

  ①、这里我们一次查询出所有的信息,需要什么信息的时候直接从查询的结果中筛选。但是如果订单和用户表都比较大的时候,这种关联查询肯定比较耗时。

  ②、我们的需求是有时候需要关联查询用户信息,这里不是一定需要用户信息的。即有时候不需要查询用户信息,我们也查了,程序进行了多余的耗时操作。

  第二种方法:分步查询,首先查询出所有的订单信息,然后如果需要用户的信息,我们在根据查询的订单信息去关联用户信息

2

3

4

//查询所有的订单信息,包括用户id

select * from orders;

//如果需要用户信息,我们在根据上一步查询的用户id查询用户信息

select * from user where id=user_id

  ①、这里两步都是单表查询,执行效率比关联查询要高很多

  ②、分为两步,如果我们不需要关联用户信息,那么我们就不必执行第二步,程序没有进行多余的操作。

  那么我们说,这第二种方法就是mybatis的懒加载。

2、什么是懒加载?

  通俗的讲就是按需加载,我们需要什么的时候再去进行什么操作。而且先从单表查询,需要时再从关联表去关联查询,能大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

  在mybatis中,resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

3、具体实例

  ①、创建实体类

    User.java

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

package

com.ys.lazyload;

import

java.util.List;

public

class

User {

//用户ID

private

int

id;

//用户姓名

private

String username;

//用户性别

private

String sex;

//一个用户能创建多个订单,用户和订单构成一对多的关系

public

List<Orders> orders;

public

List<Orders> getOrders() {

return

orders;

}

public

void

setOrders(List<Orders> orders) {

this

.orders = orders;

}

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

String getSex() {

return

sex;

}

public

void

setSex(String sex) {

this

.sex = sex;

}

@Override

public

String toString() {

return

"User [id="

+ id +

", username="

+ username +

", sex="

+ sex

+

"]"

;

}

}

    Orders.java

45

46

47

48

49

50

51

package

com.ys.lazyload;

public

class

Orders {

//订单ID

private

int

id;

//用户ID

private

int

userId;

//订单数量

private

String number;

//和用户表构成一对一的关系,即一个订单只能由一个用户创建

private

User user;

public

int

getId() {

return

id;

}

public

void

setId(

int

id) {

this

.id = id;

}

public

int

getUserId() {

return

userId;

}

public

void

setUserId(

int

userId) {

this

.userId = userId;

}

public

String getNumber() {

return

number;

}

public

void

setNumber(String number) {

this

.number = number;

}

public

User getUser() {

return

user;

}

public

void

setUser(User user) {

this

.user = user;

}

@Override

public

String toString() {

return

"Orders [id="

+ id +

", userId="

+ userId +

", number="

+ number

+

", user="

+ user +

"]"

;

}

}

  ②、创建 OrderMapper 接口和 OrderMapper.xml 文件

    

mybatis 详解(八)------ 懒加载

由于我们采用 Mapper 代理加载 xxxMapper.xml 文件,这里我们重复一下 Mapper 代理所需的条件,接口和xml文件必须满足以下几个条件:

  1、接口必须要和 xml 文件同名且在同一个包下,也就是说 xml 文件中的namespace是接口的全类名  

  2、接口中的方法名和xml 文件中定义的 id 一致

  3、接口输入参数类型要和xml 中定义的 parameterType 一致

  4、接口返回数据类型要和xml 中定义的 resultType 一致

  详细介绍参考上一篇博客:http://www.cnblogs.com/ysocean/p/7301548.html

   OrderMapper 接口

package

com.ys.lazyload;

import

java.util.List;

import

com.ys.lazyload.Orders;

import

com.ys.lazyload.User;

public

interface

OrdersMapper {

/**

* select * from order //得到user_id

* select * from user WHERE id=1   //1 是上一个查询得到的user_id的值

* @param userID

* @return

*/

//得到订单信息(包含user_id)

public

List<Orders> getOrderByOrderId();

//根据用户ID查询用户信息

public

User getUserByUserId(

int

userID);

}

  OrderMapper.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.ys.lazyload.OrdersMapper"

>

<!--

延迟加载:

select user_id from order WHERE id=

1

;

//得到user_id

select * from user WHERE id=

1

//1 是上一个查询得到的user_id的值

property:别名(属性名)    column:列名 -->

<select id=

"getOrderByOrderId"

resultMap=

"getOrderMap"

>

select * from orders

</select>

<resultMap type=

"com.ys.lazyload.Orders"

id=

"getOrderMap"

>

<id column=

"id"

property=

"id"

/>

<result column=

"number"

property=

"number"

/>

<!-- select:指定延迟加载需要执行的statement的id(根据user_id查询的statement)

如果不在本文件中,需要加上namespace

property:resultMap中type指定类中的属性名

column:和select查询关联的字段user_id

-->

<association property=

"user"

javaType=

"com.ys.lazyload.User"

column=

"user_id"

select=

"getUserByUserId"

>

</association>

</resultMap>

<select id=

"getUserByUserId"

resultType=

"com.ys.lazyload.User"

>

select * from user where id=#{id}

</select>

</mapper>

  

  ③、向 mybatis-configuration.xml 配置文件中注册 OrderMapper.xml 文件

   

mybatis 详解(八)------ 懒加载

  ④、开启懒加载配置

<!-- 开启懒加载配置 -->

<settings>

<!-- 全局性设置懒加载。如果设为‘

false

',则所有相关联的都会被初始化加载。 -->

<setting name=

"lazyLoadingEnabled"

value=

"true"

/>

<!-- 当设置为‘

true

'的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。 -->

<setting name=

"aggressiveLazyLoading"

value=

"false"

/>

</settings>

  ⑤、测试

@Test

public

void

testLazy(){

String statement =

"com.ys.lazyload.OrdersMapper.getOrderByOrderId"

;

//创建OrdersMapper对象,mybatis自动生成mapepr代理对象

OrdersMapper orderMapper = session.getMapper(OrdersMapper.

class

);

List<Orders> orders = orderMapper.getOrderByOrderId();

//第一步

for

(Orders order : orders){

System.out.println(order.getUser());

//第二步

}

session.close();

}

  我们在上面标识的第一步打一个断点,然后进行断点调试。

  第一次进入断点:注意看控制台Console现在是没有任何sql语句发出的

mybatis 详解(八)------ 懒加载

   下一步:这一步发出了第一次查询所有订单信息sql语句:select * from orders。注意只是查询订单信息,还没有进行关联查询

mybatis 详解(八)------ 懒加载

  下一步:下图已经执行了一次for循环,因为我们需要用户信息,故发出了根据用户id查询用户信息的sql语句

      注意:如果用户信息有多条,这里并不会发出多条sql语句,这是由于mybatis的一级缓存的原因,下一章会讲到。

mybatis 详解(八)------ 懒加载

4、总结

  ①、启动懒加载,mybatis初始化返回类型的时候,会返回一个cglib代理对象,该对象的关联对象(例如一对多,多对一)相关信息就会在loadpair里边,并且添加到loadmap中,cglib对象会过滤get,set ,is,"equals", "clone", "hashCode", "toString"触发方法,然后才会调用loadpair来加载关联对象的值。所以我们必须在进行懒加载的时候必须要导入相应的jar包,不然会报错。

mybatis 详解(八)------ 懒加载

   ②、其实通过上面的例子,我们很好理解懒加载的原理,就是按需加载。我们需要什么信息的时候再去查。而不是一次性查询所有的。将复杂的关联查询分解成单表查询,然后通过单表查询的结果去关联查询。

    那么不用mybatis的懒加载我们也可是实现上面的例子:

    一、定义两个mapper方法

      1、查询订单列表

      2、根据用户 id 查询用户信息

    二、先去查询第一个mapper方法,获取订单信息列表,然后放入到一个集合中

    三、如果需要用户信息,那么在程序中,我们可以遍历订单信息,得到用户id,然后通过id去查询用户信息。

    这与mybatis懒加载的区别就是,mybatis是在mapper.xml文件中配置好关联关系了,我们直接调用就好了。而自己实现的原理就是手动去建立关联关系。

继续阅读