zoukankan      html  css  js  c++  java
  • mybatis 详解(八)------ 懒加载

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

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

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

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

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

      分析:

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

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

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

    1
    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

    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
    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

    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
    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 文件

        

    由于我们采用 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 接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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

    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="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 文件

       

      ④、开启懒加载配置

    1
    2
    3
    4
    5
    6
    7
    <!-- 开启懒加载配置 -->
    <settings>
        <!-- 全局性设置懒加载。如果设为‘false',则所有相关联的都会被初始化加载。 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 当设置为‘true'的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。 -->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

      ⑤、测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @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语句发出的

      

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

      

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

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

      

    4、总结

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

      

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

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

        一、定义两个mapper方法

          1、查询订单列表

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

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

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

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

  • 相关阅读:
    Windows server 2016 解决“无法完成域加入,原因是试图加入的域的SID与本计算机的SID相同。”
    Windows Server 2016 辅助域控制器搭建
    Windows Server 2016 主域控制器搭建
    Net Framework 4.7.2 覆盖 Net Framework 4.5 解决办法
    SQL SERVER 2012更改默认的端口号为1772
    Windows下彻底卸载删除SQL Serever2012
    在Windows Server2016中安装SQL Server2016
    SQL Server 创建索引
    C#控制台或应用程序中两个多个Main()方法的设置
    Icon cache rebuilding with Delphi(Delphi 清除Windows 图标缓存源代码)
  • 原文地址:https://www.cnblogs.com/xiashenbin/p/7363475.html
Copyright © 2011-2022 走看看