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博客阅读原文】。感谢您的支持!

您可能感兴趣的

Jürgen Schuster: APEX Distinguished Community Memb... I just got back from the ODTUG Kscope18 conference in Orlando, Florida where, once again, the global APEX commu...
Lets get all Posh! Log Shipping This month’s T-SQL Tuesday is brought to us by my good friend Rob Sewell ( b | t ) and the subject is “Let’s get all P...
This week’s database news, issue 143 Google Spanner Inspires CockroachDB To Outrun It — In a database market ripe for change, Cockroach has the ...
Verizon and WWE Data Exposures Come Down to Human ... An improperly set up database can inadvertently expose whatever information it contains online. It's the kind of mi...
18c Database: Shadow Lost write protection From 18c Onwards, we can enable Lost write protection for data blocks which occurs when an I/O subsystem acknowledg...