首页 > 解决方案 > mybatis 嵌套选择 vs. 嵌套结果

问题描述

我是mybatis的初级用户,我想知道嵌套选择嵌套结果的区别是否就像子查询连接之间的区别一样,尤其是在性能方面。或者它会做一些优化?

我用的是mybatis 3.4.7版本和oracle DB。

这是一个供参考的示例:

private List<Post> posts;

    <resultMap id="blogResult" type="Blog">
        <collection property="posts" javaType="ArrayList" column="id" 
        ofType="Post" select="selectPostsForBlog"/>
    </resultMap>
    <select id="selectBlog" resultMap="blogResult">
        SELECT * FROM BLOG WHERE ID = #{id}
    </select>
    <select id="selectPostsForBlog" resultType="Post">
        SELECT * FROM POST WHERE BLOG_ID = #{id}
    </select>  

或者

    <select id="selectBlog" resultMap="blogResult">
        select
        B.id as blog_id,
        B.title as blog_title,
        B.author_id as blog_author_id,
        P.id as post_id,
        P.subject as post_subject,
      P.body as post_body,
      from Blog B
      left outer join Post P on B.id = P.blog_id
      where B.id = #{id}
    </select>

    <resultMap id="blogResult" type="Blog">
        <id property="id" column="blog_id" />
        <result property="title" column="blog_title"/>
        <collection property="posts" ofType="Post">
        <id property="id" column="post_id"/>
        <result property="subject" column="post_subject"/>
        <result property="body" column="post_body"/>
      </collection>
    </resultMap>

如果嵌套选择像子查询中仍然存在 N+1 问题?

对于在特定环境或条件下哪个表现更好,您有什么建议或经验吗?多谢 :)。

标签: oraclemybatis

解决方案


首先是一个轻微的术语说明。SQL中的子查询是查询的一部分,它本身就是一个查询,例如:

SELECT ProductName
  FROM Product 
WHERE Id IN (SELECT ProductId 
            FROM OrderItem
           WHERE Quantity > 100)

在这种情况下,查询的以下部分是子查询:

SELECT ProductId 
 FROM OrderItem
WHERE Quantity > 100

所以你在这里错误地使用了术语“子查询”。在 mybatis 文档中,使用了术语嵌套选择

mybatis中获取关联实体/集合有两种方式。这是文档的相关部分:

嵌套选择:通过执行另一个返回所需复杂类型的映射 SQL 语句。嵌套结果:通过使用嵌套结果映射来处理连接结果的重复子集。

当使用嵌套选择时,mybatis 首先执行主查询(在您的情况下selectBlog),然后对于每条记录,它执行另一个选择(因此名称nested select)以获取关联的Post实体。

使用时Nested results只执行一个查询,但它已经连接了关联数据。所以mybatis将结果映射到对象结构中。

在您的示例中,返回单个Blog实体,因此在nested select使用时会执行两个查询,但在一般情况下(如果您会获得Blogs 列表),您会遇到N+1问题。

现在让我们来处理性能。以下所有内容都假设查询已调整(因为没有丢失的索引),您正在使用连接池,数据库是并置的,基本上说您的系统在所有其他方面都已调整。

说到性能,没有一个正确的答案,你的里程可能会有所不同。您始终需要在设置中测试您的特定工作流程。考虑到影响性能的因素有很多,比如数据分布(想想每个博客的最大/最小/参数)、数据库中记录的大小(想想博客和帖子中数据字段的数量和大小)、数据库参数(例如磁盘类型和速度、可用于数据集缓存的内存量等)可能没有单一的答案,只有一些一般性的观察结果。

但是,如果我们查看性能范围两端的案例,我们可以理解性能差异。喜欢看到nested select显着优于的情况join,反之亦然。

对于集合获取连接在大多数情况下应该更好,因为网络延迟来做N+1请求计数。

当主表中的记录引用其他表并且其他表的基数不大并且其他表中的记录的大小很大时,可能更好的一种情况nested select是一对多关联。

例如,让我们考虑Blog有一个category引用categoriestable 的属性,它可能具有这些值之一Science, Fashion, News。让我们想象一下博客列表是由一些过滤器选择的,比如博客标题中的关键字。如果结果包含比方说 500 个项目,那么大多数相关类别将是重复的。

如果我们通过连接选择它们,结果集中的每条记录都将包含Category数据字段(提醒一下,它们中的大多数都是重复的,我们有很多数据在Category记录中)。

如果我们使用嵌套选择来选择它们,我们将执行查询以按类别 id 为每条记录获取类别,这里 mybatis 会话缓存开始发挥作用。在SqlSession每次 mybatis 执行查询期间,它会将其结果存储在会话缓存中,因此它不会对数据库执行重复请求,而是从缓存中获取它们。这意味着在 mybatis 通过 id 为第一条记录检索到某个类别后,它将重新用于它处理的记录集中的所有其他记录。

在上面的示例中,我们最多可以向数据库发出 4 个请求,但是通过网络传递的数据量减少可能会超过执行 4 个请求的需要。


推荐阅读