mybatis nested select vs. 嵌套结果

mybatis nested select vs. nested results

我是mybatis的初级用户,不知道嵌套select嵌套结果的区别是否是就像 sub-queryjoin 之间的区别一样,尤其是在性能上。或者它会做一些优化?

我用的是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>

嵌套selectlike子查询是否还有N+1个问题?

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

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

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

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

SELECT ProductId 
 FROM OrderItem
WHERE Quantity > 100

所以你在这里使用术语 "subquery" 不正确。在 mybatis 文档中使用术语 嵌套 select

在mybatis中获取关联entities/collections有两种方式。这是 documentation 的相关部分:

Nested Select: By executing another mapped SQL statement that returns the complex type desired. Nested Results: By using nested result mappings to deal with repeating subsets of joined results.

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

当使用Nested results时,只执行一个查询,但它已经加入了关联数据。所以mybatis map结果到object结构

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

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

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

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

对于 collection 在大多数情况下获取连接应该更好,因为执行 N+1 请求计数的网络延迟。

nested select 可能更好的一种情况是 one-to-many 关联,当主 table 中的记录引用其他一些 table 和其他 one-to-many 的基数时=87=] 不大,而另一个 table 中的记录大小很大。

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

如果我们 select 他们加入结果集中的每条记录将包含 Category 数据字段(提醒一下,其中大部分都是重复的,我们在 Category 记录).

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

在上面的示例中,我们将对数据库执行最多 4 个请求,但通过网络传递的数据量减少可能会增加执行 4 个请求的需求。