oracle - 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 问题?
对于在特定环境或条件下哪个表现更好,您有什么建议或经验吗?多谢 :)。
解决方案
首先是一个轻微的术语说明。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
使用时会执行两个查询,但在一般情况下(如果您会获得Blog
s 列表),您会遇到N+1
问题。
现在让我们来处理性能。以下所有内容都假设查询已调整(因为没有丢失的索引),您正在使用连接池,数据库是并置的,基本上说您的系统在所有其他方面都已调整。
说到性能,没有一个正确的答案,你的里程可能会有所不同。您始终需要在设置中测试您的特定工作流程。考虑到影响性能的因素有很多,比如数据分布(想想每个博客的最大/最小/参数)、数据库中记录的大小(想想博客和帖子中数据字段的数量和大小)、数据库参数(例如磁盘类型和速度、可用于数据集缓存的内存量等)可能没有单一的答案,只有一些一般性的观察结果。
但是,如果我们查看性能范围两端的案例,我们可以理解性能差异。喜欢看到nested select
显着优于的情况join
,反之亦然。
对于集合获取连接在大多数情况下应该更好,因为网络延迟来做N+1
请求计数。
当主表中的记录引用其他表并且其他表的基数不大并且其他表中的记录的大小很大时,可能更好的一种情况nested select
是一对多关联。
例如,让我们考虑Blog
有一个category
引用categories
table 的属性,它可能具有这些值之一Science, Fashion, News
。让我们想象一下博客列表是由一些过滤器选择的,比如博客标题中的关键字。如果结果包含比方说 500 个项目,那么大多数相关类别将是重复的。
如果我们通过连接选择它们,结果集中的每条记录都将包含Category
数据字段(提醒一下,它们中的大多数都是重复的,我们有很多数据在Category
记录中)。
如果我们使用嵌套选择来选择它们,我们将执行查询以按类别 id 为每条记录获取类别,这里 mybatis 会话缓存开始发挥作用。在SqlSession
每次 mybatis 执行查询期间,它会将其结果存储在会话缓存中,因此它不会对数据库执行重复请求,而是从缓存中获取它们。这意味着在 mybatis 通过 id 为第一条记录检索到某个类别后,它将重新用于它处理的记录集中的所有其他记录。
在上面的示例中,我们最多可以向数据库发出 4 个请求,但是通过网络传递的数据量减少可能会超过执行 4 个请求的需要。
推荐阅读
- ssh - 为什么我通过 2 个不同的 ssh 会话创建的文件夹在 GCP 的同一个云实例上彼此不可见?
- javascript - 在 NodeJS 模块中导出变量之前,我可以等待承诺解析(HTTP 请求)吗?
- typescript - TypeScript 中的“as”运算符优先级
- java - BaseActivity 中注入器上的棘手空指针异常
- python - 如何在组中获得表达式的单个部分?
- postgresql - 关系“表名”不存在,但在 FROM 中设置
- hive - MAX() OVER () with order by 2 columns 函数产生不一致的结果 Hive
- python - 尝试在 CSV 中查找和替换时内存不足
- c# - 为什么 DI 为两个不同的范围返回相同的 DbContext?
- c++ - 如何查看向量内部的内容?