【问题标题】:mybatis nested select vs. nested resultsmybatis 嵌套选择 vs. 嵌套结果
【发布时间】:2019-01-11 04:02:00
【问题描述】:

我是mybatis的初级用户,想知道嵌套选择嵌套结果的区别是不是就像子查询的区别 vs. join,尤其是在性能方面。还是会做一些优化?

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

如果嵌套 select like 子查询还有 N+1 的问题?

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

【问题讨论】:

  • 只是提一下 - 在第一个示例中,您正在执行 INNER JOIN,第二个示例不一样。另一方面,第二个选择更加灵活。而且,IMO,oracle 会将两者转换为相同的选择(假设嵌套选择中的 INNER JOIN)。

标签: oracle mybatis


【解决方案1】:

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

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

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

SELECT ProductId 
 FROM OrderItem
WHERE Quantity > 100

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

【讨论】:

  • 嗨!我想知道第三种方法是否更好(尽管 MyBatis 不支持):假设我们想要具有某些条件的博客及其帖子。第一次查询SELECT blog_id, othercolumns FROM blogs WHERE mycondition。第二个查询SELECT comment_id, blog_id, others FROM comments WHERE blog_id IN (?,?,...,?) 插入我们在第一个查询中获得的blog_ids 列表。在这种情况下,我们只需要 2 个查询!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-03
  • 1970-01-01
  • 2011-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-16
相关资源
最近更新 更多