Mybatis的关联的嵌套 Select 查询中的N+1问题

什么是N+1问题?

  • 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。

  • 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。

什么情况会存在N+1问题?

首先单表查询时不会存在N+1问题的,存在N+1问题的情况就是存在关联嵌套查询的时候。

举个例子,场景:一个电商系统,查询所有的客户信息(Customer)。

假如此时如果我们的客户信息表是与订单信息表关联的。

//客户信息表
public class Customer {
 
	private String customerId;
	private String cutomerName;
    //订单表,一个客户可能有多个订单
	private List<Order> orders;
	
	public String getCustomerId() {
		return customerId;
	}
	public void setCustomerId(String customerId) {
		this.customerId = customerId;
	}
	public String getCutomerName() {
		return cutomerName;
	}
	public void setCutomerName(String cutomerName) {
		this.cutomerName = cutomerName;
	}
	public List<Order> getOrders() {
		return orders;
	}
	public void setOrders(List<Order> orders) {
		this.orders = orders;
	}
	
}
//订单表
public class Order {
 
	private String orderId;
	private String customerId;
	private int num;
	private double total;
	public String getOrderId() {
		return orderId;
	}
	public void setOrderId(String orderId) {
		this.orderId = orderId;
	}
	public String getCustomerId() {
		return customerId;
	}
	public void setCustomerId(String customerId) {
		this.customerId = customerId;
	}
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	public double getTotal() {
		return total;
	}
	public void setTotal(double total) {
		this.total = total;
	}
	
}

当我们这个时候要查询客户信息时,如果mapper文件写法如下:

<resultMap type="Customers" id="customersResultMap" >
    <id property="customerId" column="customer_id" />
	<result property="customerName" column="customer_name" javaType="string"/>
	<collection property="orders" ofType="Order" select="getCustOrders" column="customerId" />
</resultMap>
-- 查询所有客户信息
<select id="querAllCustomers" parameterType="java.util.HashMap" resultMap="customersResultMap">
	select customer_id, customer_name from tb_customers
</select>

-- 查询客户的所有订单
<select id="getCustOrders" resultType="Order">
  SELECT * FROM tb_order WHERE customer_id = #{customerId}
</select>

当我们查询客户信息时,系统先调用querAllCustomers()方法,执行如下过程

select customer_id, customer_name from tb_customers 1次

SELECT * FROM tb_order WHERE customer_id = 1

SELECT * FROM tb_order WHERE customer_id = 2

SELECT * FROM tb_order WHERE customer_id = 3

...

SELECT * FROM tb_order WHERE customer_id = n

综上所述即:执行了一个单独的 SQL 语句来获取结果的一个列表,对列表返回的结果,执行一个 select 查询语句来为每条记录加载详细信息。

当客户信息表数据量非常大,每个客户的订单量也非常大的时候,对数据库查询性能消耗可想而知。

 

解决办法:关联的嵌套结果映射查询,通过SQL语句一次性查询出来。

修改mapper配置文件如下:

<resultMap type="Customers" id="customersResultMap" >
	<id property="customerId" column="customer_id" />
	<result property="customerName" column="customer_name" javaType="string"/>
	<collection property="orders" ofType="Order">
	  <id column="order_id" property="orderId"/>
	  <result property="customerId" column="customer_id" javaType="string"/>
	  <result property="num" column="num" javaType="string"/>
	  <result property="total" column="total" javaType="string"/>
	</collection>
</resultMap>

<select id="querAllCustomers" parameterType="java.util.HashMap" resultMap="customersResultMap">
	select cm.customer_id, cm.customer_name 
	  from tb_customers cm
	  left join tb_order od 
	         on cm.customer_id = od.customer_id
</select>

 

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值