zoukankan      html  css  js  c++  java
  • 在mybatis框架中,延迟加载与连表查询的差异

    1.引子     

          mybatis的延迟加载,主要应用于一个实体类中有复杂数据类型的属性,包括一对一和一对多的关系(在xml中用collection、association标签标识)。这个种属性往往还对应着另一个数据表,而实际查询的需求不一定需要这个的表的数据,那么此时延迟加载相对于连表查询就有很大的优势,达到了按需加载的目的。这对提高访问速度和降低系统资源耗费有着很大的意义。

    2.连表查询

    背景: 用户实体类User中包含有一个为角色实体类Role类型的属性role,及对应角色表主键id的roleUser整型属性

    需求:用角色id和用户id列表,查找出用户信息,及其所角色名称

    分析: 角色名称不在用户表中,用户表中只有角色表的主键id,那么此就必须要根据roleUser到角色表中再查询。为了减少查询次数,实际上我们进行连表查询。

       实体类User中的部分属性

       private Integer id; // id
        private String userCode; // 用户编码
        private String userName; // 用户名称
        private String userPassword; // 用户密码
        private Integer userRole; // 用户角色id,和角色表smbms_role的id字段关联对应
        private Role role; //角色   一对一 对应关系
      private List<Address> addressList; //地址   一对多

       实体类Role中的属性

      private Integer id; // id
        private String roleCode; // 角色编码
        private String roleName; // 角色名称
        private Integer createdBy; // 创建者
        private Date creationDate; // 创建时间
        private Integer modifyBy; // 更新者
        private Date modifyDate;// 更新时间

    xml中的sql语句映射文件

    <!-- 演示 一对一 association 的连表查询 start -->
    
        <resultMap type="Role" id="roleMap">
            <id property="id" column="r_id" />
            <result property="roleCode" column="roleCode" />
            <result property="roleName" column="roleName" />
        </resultMap>
        <resultMap type="User" id="userRoleResult">
            <id property="id" column="id" />
            <result property="userCode" column="userCode" />
            <result property="userName" column="userName" />
            <association property="role" javaType="Role" resultMap="roleMap" />
        </resultMap>
        <!-- 根据用户表中角色id 和角色表做联表查询 -->
        <select id="getUserListByRoleId" parameterType="int" resultMap="userRoleResult">
            select u.* ,r.id as r_id,r.roleCode,r.roleName from
            smbms_user
            u,smbms_role r
            where u.userRole = #{userRoleId} and u.userRole = r.id
        </select>

    <!-- 演示 一对一 association 的连表查询 end -->

     测试方法

      @Test
        public void selectUserByIdsAndRoleId()
        {
            List<User> users = sqlSession.getMapper(UserMapper.class).selectUserByIdsAndRoleId(3, Arrays.asList(4, 3, 7,12));
            for (User userElement : users)
            {
                System.out.println(userElement);
            }
        }

     3. 延迟加载

    1)延迟加载配置

    延迟加载可以在主配置文件mybatis-config.xml中配置,在这里的配置是全局配置,默认对所有查询SQL进行延迟加载处理。

    <settings>
    <!--启用延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true" />
    <!--关闭积极加载 -->
    <setting name="aggressiveLazyLoading" value="false" />
    </settings>

    另外如果对全局配置不满意,可以在具体的SQL映射语句中添加collect或assocation标签中添加属性fetchType="lazy"(延迟加载)或fetchType="eager"(立即加载),

    此处SQL加载方式将覆盖全局配置中的SQL加载方式,如伪代码

    <collection property="addressList" 
    ofType="Address" column="id"
        fetchType="lazy|eager"/>

    2)延迟加载SQL映射

    背景: 用户实体类User中包含有一个List集合且元素类型为Address的属性addressList,用户表的id与地址表的userId有对应的主外键关系

    需求:使用用户id查找出用户信息及相关地址信息(但地址可能不会总是被使用)

    分析: 地址信息不总会用到,如果某次查询刚好是这种情况,也就没有必要再去查询地址表了。这时就可以使用延迟加载技术,实现按需动态加载资源。

    xml中的sql映射代码

        <!--演示 一对多 collection -->
        <resultMap type="User" id="userAddressResult">
            <id property="id" column="id" />
            <result property="userCode" column="userCode" />
            <result property="phone" column="phone" />
            <result property="userName" column="userName" />
            <!-- 在collection标签内需要注意这几个属性的设置
                1."column"属性,此属性是两张表关联的关键,此字段的值能唯一确定另一张从表的对应记录行,此字段与从表的外键一一对应,此字段一般为主表的主键,
                    相当于连表查询中根据主外键建立两张的的关联关系,
                    如" from smbms_user u inner join smbms_address a on u.id = a.userId "
                2."select"属性,此属性指定可能需要查询SQL的id,
                3."fetchType"属性,指定为"lazy"(懒加载)实现延迟加载效果(若全局配置已配置过延迟加载,也可在此处不写此属性,
                   mybatis也会默认读到全局配置中的延迟加载项),而"eager"表示立即加载 
    4.ofType可以不写,lazyFindAddressListByUserId这个查询本身已经指定了返回数据类型
    --> <collection property="addressList" ofType="Address" column="id" select="lazyFindAddressListByUserId" fetchType="lazy" /> </resultMap> <!-- 延迟按需加载sql语句(即此sql可能不会使用) --> <select id="lazyFindAddressListByUserId" parameterType="int" resultType="Address"> <!-- 如果确实需要此SQL查询,则上面<collection>标签中的"column='id'" 的id字段的对应值将传到此处的'userId=#{id}'中作为参数 ,进一步查询地址表smbms_address --> <!-- 若此处SQL确实被执行了,那么就相当于连表查询语句 select a.* ,u.* from smbms_user u inner join smbms_address a on u.id = a.userId and u.id = #{id} --> select * from smbms_address where userId=#{id} </select> <!-- 根据用户id获得 用户信息及多个地址信息 --> <select id="getAddressListByUserId" parameterType="Integer" resultMap="userAddressResult"> select * from smbms_user where id = #{userId} </select>

    3)延迟加载测试

     不使用addressList地址信息的测试方法

     @Test
        public void getAddressListByUserId()
        {
            List<User> users = sqlSession.getMapper(UserMapper.class).getAddressListByUserId(5);
            for (User userElement : users)
            {
              //不使用addressList属性
                 System.out.println(userElement.getPhone() +" "+userElement.getUserCode());
               //   System.out.println(userElement);
            }
        }

     控制台显示只有一个sql语句,则实际上只查询了一张用户表

     使用addressList地址信息的测试方法

     @Test
        public void getAddressListByUserId()
        {
            List<User> users = sqlSession.getMapper(UserMapper.class).getAddressListByUserId(5);
            for (User userElement : users)
            {
                // 使用addressList属性
                // System.out.println(userElement.getPhone() +" "+userElement.getUserCode());
                System.out.println(userElement);
            }
        }

     控制台显示只有两条sql语句,则实际上只先查询了用户表,再用用户id去查询地址表

     4.总结

           (1)连表查询,在任何情况下都要同时查询多张表的数据 ;  而延迟加载,只有在项目中确实使用到了另一个从表的数据,才会去查询第二(N)张表的数据。

           (2)连表查询同时操作多张表,如果多张表的字段名有相同的,为了正确的将字段值映射到实体类的属性上,可能要给相同的字段名取一个别名,并对结果集进行相应的自定义映射;

    延迟加载相当于将一次连表查询操作分解为多次单表查询操作,既然是单表操作也就不可能出现字段名相同的情况,只要数据表的字段名和实体类的属性名按照同样的驼峰命名规则,就不需要对结果集进行自定义映射(mybatis会做默认的结果集映射)。

           (3)对实体类的复杂数据类型属性做SQL映射,两者都会使用到"association"或"collection"标签。而连表查询、延迟加载在"association"或"collection"标签中的属性设置有些区别,延迟加载特别的要使用"column"、"select"、"fetchType"这三个属性。连表查询只能解决一对一的级联关系,而延迟加载既能处理一对一的级联,也能解决一对多的级联查询问题。

  • 相关阅读:
    View Focus的处理过程及ViewGroup的mFocused字段分析
    Android按键事件处理流程 -- KeyEvent
    Android中将xml布局文件转化为View树的过程分析(下)-- LayoutInflater源码分析
    Android中将xml布局文件转化为View树的过程分析(上)
    IntentService源码分析
    Android源码分析之SharedPreferences
    进程间传递文件描述符
    Epoll在LT和ET模式下的读写方式
    gdb
    Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment
  • 原文地址:https://www.cnblogs.com/gocode/p/lazyload-and-join-query-in-mybatis.html
Copyright © 2011-2022 走看看