zoukankan      html  css  js  c++  java
  • mybatis-高级结果映射之一对一

    mybatis的高级结果映射可以很轻松的帮助我们处理一对一, 一对多的数据关系。

    1 数据准备

    1.1 数据库

    创建以下的名为 mybatis 的数据库, 并在其下创建4个表。

    数据库结构

    在此就不贴出来建表的 SQL 语句了 , 感兴趣的可以去我的 Github:mybatis-mapping 中获取。

    1.2 实体类, 接口和XML

    使用 mybatis-代码生成器 生成相应的实体类, 接口和XML。

    代码结构

    以上为生成的项目结构。

    2 一对一映射

    创建的表中, author 和 blog 就是一对一的关系。

    我们希望找到一个blog, 然后就会把它的作者信息也关联出来。

    2.1 resultType 方式一

    注意:resultType方式要求获取到的列和成员变量名一致。

    2.1.1 创建对象

    创建一个类BlogCustom, 该类继承于 Blog 类, 在该类上添加 Author 对象作为成员变量

    BlogCustom

    2.1.2 创建接口方法和XML 语句

        BlogBO selectBoById(int id);
        /**
         * 根据博客的 id 获取博客及作者的信息
         * @param id
         * @return
         */
        BlogCustom selectCutomById(int id);
    

    对应的 XML 语句:

      <select id="selectCutomById" parameterType="java.lang.Integer" resultType="com.homejim.mybatis.entity.BlogCustom">
       SELECT
        b.id,
        b.title,
        b.author_id AS authorId,
        a.id AS "author.id",
        a.username AS "author.username",
        a.password AS "author.password" ,
        a.email AS "author.email"
      FROM blog b LEFT JOIN author a ON b.author_id=a.id
     where b.id = #{id,jdbcType=INTEGER}
      </select>
      
    

    通过 author.username 这种方式可以设定 author 对象中的 username 属性。

    2.1.3 测试

        @Test
        public void testSelectCustomById() {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
            BlogCustom blogCustom = blogMapper.selectCutomById(1);
            System.out.println(ToStringBuilder.reflectionToString(blogCustom, ToStringStyle.MULTI_LINE_STYLE));
        }
    

    运行后的结果

    resultType方式一结果

    有时候, 我们获取的另外的属性不多, 则我们也可以选择下面的方式二。

    2.2 resultType 方式二

    2.2.1 创建对象

    创建一个类BlogBO, 该类继承于 Blog 类, 在该类上添加 Author 中的成员变量作为该类自己的成员变量。

    BlogBO

    2.2.2 创建接口方法和XML 语句

    接口方法

        /**
         * 根据博客的 id 获取博客及作者的信息
         * @param id
         * @return
         */
        BlogBO selectBoById(int id);
    

    XML 对应的 SQL , 其中 resultType 是上面定义的实体对象。

      <select id="selectBoById" parameterType="java.lang.Integer" resultType="com.homejim.mybatis.entity.BlogBO">
        select
         b.id, b.title, b.author_id as authorId, a.id as userId, a.username, a.email
        from blog b left join author a on b.author_id=a.id
        where b.id = #{id,jdbcType=INTEGER}
      </select>
    

    2.2.3 测试

    创建一个测试例子。

        @Test
        public void testSelectBoById() {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
            BlogBO blogBO = blogMapper.selectBoById(1);
            System.out.println(ToStringBuilder.reflectionToString(blogBO, ToStringStyle.MULTI_LINE_STYLE));
        }
    

    测试结果

    resultType方式二结果

    2.3 resultMap 方式

    2.3.1 创建对象

    创建一个类BlogCustom, 该类继承于 Blog 类, 在该类上添加 Author 对象作为成员变量

    image

    2.3.2 创建对应 resultMap

    对应创建一个 resultMap

      <resultMap id="BOResultMap" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap">
        <result column="username" jdbcType="VARCHAR" property="author.username" />
        <result column="user_id" jdbcType="VARCHAR" property="author.id" />
        <result column="email" jdbcType="VARCHAR" property="author.email" />
      </resultMap>
    

    2.3.3 创建接口方法和XML 语句

        /**
         * 根据博客的 id 获取博客及作者的信息, resultMap 方式
         * @param id
         * @return
         */
        BlogCustom selectCutomByIdMap(int id);
    

    SQL 语句得出的列名与 BOResultMap 一致。

      <select id="selectCutomByIdMap" parameterType="java.lang.Integer" resultMap="BOResultMap">
       SELECT
        b.id,
        b.title,
        b.author_id AS authorId,
        a.id AS "user_id",
        a.username,
        a.email
      FROM blog b LEFT JOIN author a ON b.author_id=a.id
     where b.id = #{id,jdbcType=INTEGER}
      </select>
      
    

    2.3.4 测试

        /**
         *  resultMap 方式一测试
         */
        @Test
        public void testSelectCustomByIdMap() {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
            BlogCustom blogCustom = blogMapper.selectCutomByIdMap(1);
            System.out.println(ToStringBuilder.reflectionToString(blogCustom, ToStringStyle.MULTI_LINE_STYLE));
        }
    

    得出的结果

    resultMap结果

    其实该类型也可以配置对应 BlogBO 类对应的方式, 在此不做过多的讲解。

    2.4 resultMap + association 方式

    2.4.1 创建对象

    创建一个类BlogCustom, 该类继承于 Blog 类, 在该类上添加 Author 对象作为成员变量

    BlogCustom

    2.4.2 创建 resultMap

      <resultMap id="CustomResultMap" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap">
         <association property="author" javaType="com.homejim.mybatis.entity.Author">
           <id column="user_id" jdbcType="INTEGER" property="id" />
           <result column="username" jdbcType="VARCHAR" property="username" />
           <result column="email" jdbcType="VARCHAR" property="email" />
         </association>
      </resultMap>
    

    或者, 可以引用别的 Mapper 中的结果集

      <resultMap id="CustomResultMap" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap">
         <association property="author" javaType="com.homejim.mybatis.entity.Author" resultMap="com.homejim.mybatis.mapper.AuthorMapper.BaseResultMap">
         </association>
      </resultMap>
    

    2.4.3 创建接口方法和XML 语句

        /**
         * 根据博客的 id 获取博客及作者的信息, resultMap + association方式
         * @param id
         * @return
         */
        BlogCustom selectCutomByIdAMap(int id);
    

    SQL 语句

    <select id="selectCutomByIdAMap" parameterType="java.lang.Integer" resultMap="CustomResultMap">
       SELECT
        b.id,
        b.title,
        b.author_id AS authorId,
        a.id AS "user_id",
        a.username,
        a.email
      FROM blog b LEFT JOIN author a ON b.author_id=a.id
     where b.id = #{id,jdbcType=INTEGER}
      </select>
    

    2.4.4 测试

        /**
         *  resultMap + association 方式测试
         */
        @Test
        public void testSelectCustomByIdAMap() {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
            BlogCustom blogCustom = blogMapper.selectCutomByIdAMap(1);
            System.out.println(ToStringBuilder.reflectionToString(blogCustom, ToStringStyle.MULTI_LINE_STYLE));
        }
    

    结果

    resultMap Association 结果

    2.5 resultMap + association 嵌套查询

    以上几种方法都是通过 left join 的 SQL 语句获取多个对象的结果。 还可以经过简单的 SQL 语句, 多次查询而转化为我们需要的结果。

    2.5.1

    2.5.1 创建对象

    创建一个类BlogCustom, 该类继承于 Blog 类, 在该类上添加 Author 对象作为成员变量

    结果

    2.5.2 创建 resultMap

      <resultMap id="blogAuthorMap" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap">
        <association property="author" column="author_id" select="com.homejim.mybatis.mapper.AuthorMapper.selectById" fetchType="eager" />
      </resultMap>
    

    该结果集与之前的不同了, association 标签中使用了 select 属性。

    select: 另一个查询的 id, mybatis 会额外执行这个查询来获取嵌套的结果

    column: 列名(别名), 将主查询中的列作为嵌套查询的参数, 多个参数使用逗号(英文)分隔开。

    fetchType: 数据加载的方式, 可选择为 lazy(延迟加载) 或者 eager(积极加载), 该配置会覆盖全局的 lazyLoadingEnabled 配置。

    2.5.3 创建接口方法和XML 语句

        /**
         * 根据博客的 id 获取博客及作者的信息, resultMap + association嵌套方式
         * @param id
         * @return
         */
        BlogCustom selectBlogAndAuthorByIdSelect(int id);
    

    获取的数据是博客和用户的信息, 以下的 SQL 只是获取博客信息, 用户信息通过 com.homejim.mybatis.mapper.AuthorMapper.selectById 获取。

      <select id="selectBlogAndAuthorByIdSelect" parameterType="java.lang.Integer" resultMap="blogAuthorMap">
       SELECT
        b.id,
        b.title,
        b.author_id
      FROM blog b
     where b.id = #{id,jdbcType=INTEGER}
      </select>
    

    com.homejim.mybatis.mapper.AuthorMapper.selectById 是一个全限定名, 即 AuthorMapper 下的 selectById 方法

        /**
         * 嵌套查询使用的方法
         * @param id
         * @return
         */
        Author selectById(Integer id);
    

    对应的 SQL

      <select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from author
        where id = #{id}
      </select>
    

    2.5.4 测试

    使用时, 调用 selectBlogAndAuthorByIdSelect 方法即可。

        /**
         *  resultMap + association 嵌套查询方式测试
         */
        @Test
        public void testSelectBlogAndAuthorByIdSelect() {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
            BlogCustom blogCustom = blogMapper.selectBlogAndAuthorByIdSelect(1);
            System.out.println(ToStringBuilder.reflectionToString(blogCustom, ToStringStyle.MULTI_LINE_STYLE));
    
            Assert.assertNotNull(blogCustom);
            Assert.assertNotNull(blogCustom.getAuthor());
    
        }
    

    输出, 会发送两次 SQL 语句。

    两次SQL

    可以看到, 上面的结果示意图中, 发送了两次 SQL 。

    2.5.5 延迟加载

    如果是一个对象中只是包含一两个对象, 使用上面的方式还好。 但是如果包含有很多, 那要一次性发送很多次 SQL, 性能上就会很有影响。延迟加载可以解决此类的问题。

    延迟加载就是说,只有在调用内部的对象时, 才会把获取该对象的 SQL 发送出去。

    更改结果集

      <resultMap id="blogAuthorMapLazy" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap">
        <association fetchType="lazy" property="author" column="author_id" select="com.homejim.mybatis.mapper.AuthorMapper.selectById" />
      </resultMap>
    

    将上面的查询中的结果集更改 resultMap="blogAuthorMapLazy"

      <select id="selectBlogAndAuthorByIdSelect" parameterType="java.lang.Integer" resultMap="blogAuthorMapLazy"> <!--resultMap="blogAuthorMap"-->
        SELECT
        b.id,
        b.title,
        b.author_id
      FROM blog b
     where b.id = #{id,jdbcType=INTEGER}
      </select>
    

    更改延迟加载总开关

     <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
    

    测试

    延迟加载结果

    注意: 延迟加载是在 SqlSession 的声明周期内的, 如果超出该声明周期, 如 spring 中, 只能在 Service 层使用延迟加载的对象, 如果返回Controller层在获取延迟加载属性, 则会抛出异常。

    有时候, 我们配置了延迟加载, 但是却想要一次性加载, 怎么办?

    有一个配置属性可以帮我们解决 lazyLoadTriggerMethods, 它的默认配置如下:

    <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
    

    就是说我们使用上面配置中的任何一个方法(上面的是默认的, 我们可以不配置), 就可以加载属性啦。

    测试

        /**
         *  resultMap + association 嵌套查询方式测试(延迟加载不延迟lazyLoadTriggerMethods)
         */
        @Test
        public void testSelectBlogAndAuthorByIdSelectTrigger() {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
            BlogCustom blogCustom = blogMapper.selectBlogAndAuthorByIdSelect(1);
            blogCustom.equals(null);
            Assert.assertNotNull(blogCustom);
            sqlSession.close();
            System.out.println("开始使用author对象");
            Assert.assertNotNull(blogCustom.getAuthor());
    
        }
    

    结果

    触发方法加载

    3. 代码

    本来还要写的一对多, 鉴别器的, 但由于篇幅的原因, 后续继续吧。

    我的 Github:mybatis-mapping

    一起学 mybatis

    你想不想来学习 mybatis? 学习其使用和源码呢?那么, 在博客园关注我吧!!

    我自己打算把这个源码系列更新完毕, 同时会更新相应的注释。快去 star 吧!!

    mybatis最新源码和注释

    github项目

  • 相关阅读:
    k8s是什么
    jmeter性能测试的策略
    面试的灵魂拷问:你最大的缺点是什么?
    面试的灵魂拷问:你做过的最令你自豪的事情是什么?
    面试的灵魂拷问:你犯过的最大的失误是什么?
    5-django rest framework,搭建api,这是最重要的章节
    4-restfulapi的介绍,vue代码结构
    组合恒等式证明——「Zeilberger 老爷子的 T 恤上写了啥?」
    「NOI2020」 美食家 【矩阵快速幂】
    [CEOI2014] The Wall【最短路】
  • 原文地址:https://www.cnblogs.com/homejim/p/9785802.html
Copyright © 2011-2022 走看看