mybatis-高级结果映射之一对一(多种方式, 有没提到的你找我)

存储架构 2018-10-14 阅读原文

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37139197/article/details/83045858

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 对象作为成员变量

2.1.2 创建接口方法和XML 语句

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

对应的 XML 语句:

   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}
  

通过 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));
    }

运行后的结果

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

2.2 resultType 方式二

2.2.1 创建对象

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

2.2.2 创建接口方法和XML 语句

接口方法

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

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

    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}
  

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));
    }

测试结果

2.3 resultMap 方式

2.3.1 创建对象

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

2.3.2 创建对应 resultMap

对应创建一个 resultMap

    
    
    
  

2.3.3 创建接口方法和XML 语句

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

SQL 语句得出的列名与 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}
  

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));
    }

得出的结果

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

2.4 resultMap + association 方式

2.4.1 创建对象

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

2.4.2 创建 resultMap

     
       
       
       
     
  

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

     
     
  

2.4.3 创建接口方法和XML 语句

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

SQL 语句

   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}
  

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));
    }

结果

2.5 resultMap + association 嵌套查询

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

2.5.1 创建对象

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

2.5.2 创建 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
    b.id,
    b.title,
    b.author_id
  FROM blog b
 where b.id = #{id,jdbcType=INTEGER}
  

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

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

对应的 SQL

    select
    
    from author
    where id = #{id}
  

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 。

2.5.5 延迟加载

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

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

更改结果集

    
  

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

 
    SELECT
    b.id,
    b.title,
    b.author_id
  FROM blog b
 where b.id = #{id,jdbcType=INTEGER}
  

更改延迟加载总开关

测试

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

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

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

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

测试

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

CSDN博客

责编内容by:CSDN博客阅读原文】。感谢您的支持!

您可能感兴趣的

Create a new postgres table but with averages I'm a SQL newbie. I have a postgres table that has datetime, and values. with subminute entries. I want to create a ne...
Exadata 删除,添加物理磁盘过程 Exadata关于磁盘部分,新增加了lun,celldisk,griddisk的概念,这里简单介绍一下,如下图: 我们可以看到一个物理硬盘添加到Cell之后,会自动创建LUN,LUN对应的是创建一个celldisk,一个cel...
Android database backup A few months ago, I made an Android app. But in the time, I didn't think that I would ever need to make a backup of m...
MongoDB和WiredTiger MongoDB 是目前主流的 NoSQL 数据库之一,与关系型数据库和其它的 NoSQL 不同,MongoDB 使用了面向文档的数据存储方式,将数据以类似 JSON 的方式存储在磁盘上,因为项目上的一些历史遗留问题,作者在最近的工作中也不得...
MongoDB源码概述——内存管理和存储引擎... 数据存储: 之前在介绍Journal的时候有说到为什么MongoDB会先把数据放入内存,而不是直接持久化到数据库存储文件,这与MongoDB对数据库记录文件的存储管理操作有关。MongoDB采用操作系统底层提供的内存文件...