1. 程式人生 > >MyBatis舉例說明資料庫查詢1+n或者n+1問題

MyBatis舉例說明資料庫查詢1+n或者n+1問題

現象:

網上在解釋程式開發過程中常見的1+n或者n+1問題時,有的解釋不太詳細,初學者可能不太好理解;
現以Mybatis為例,解釋下該問題,如下:

1、場景:查詢出客戶基礎資訊及其訂單資訊
1.1 BOM模型設計:

       Customer.java

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;
	}
	
}

  Order.java

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;
	}
	
}

1.2 Mybatis mapper.xml配置檔案:

-- 查詢客戶資訊
<resultMap type="Customers" id="customersResultMap" >
	<id column="customerId"/>
	<result property="customerName" column="customerName" javaType="string"/>
	<collection property="orders" ofType="Order" select="getCustOrders" column="{customerId=customerId}" />
</resultMap>
<select id="loadCustomers" statementType="PREPARED" parameterType="java.util.HashMap" resultMap="customersResultMap">
	select customerId, customerName from tb_customers
</select>

-- 查詢訂單資訊
<resultMap type="Order" id="customersResultMap" >
	<id column="customerId"/>
	<result property="orderId" column="orderId" javaType="string"/>
  <result property="num" column="num" javaType="string"/>
  <result property="total" column="total" javaType="string"/>
</resultMap>
<select id="getCustOrders" statementType="PREPARED" parameterType="java.util.HashMap" resultMap="customersResultMap">
	select customerId, orderId, num, total 
	  from tb_order 
	 where customerId = #{customerId}
</select>

2、結果說明 
系統會先執行 loadCustomers 查詢語句,根據該查詢的結果遍歷執行 getCustOrders 查詢
即會造成如下執行過程
select customerId, customerName from tb_customers  1 次

select customerId, orderId, num, total from tb_order where customerId = 1
select customerId, orderId, num, total from tb_order where customerId = 2
select customerId, orderId, num, total from tb_order where customerId = 3
... ...                          ... ...
select customerId, orderId, num, total from tb_order where customerId = n
即造成最終執行結果為1+n次 也就是網上所說的1+n問題或者n+1問題 其實說1+n更確切些
造成的結果也就是給資料庫伺服器帶來不必要的壓力,可以想象我如果從tb_customers查詢出來的數量為1000, 每個人名下有20個訂單明細,資料庫的查詢量可想而知,那如果數量更大呢?

但如何才能避免類似的問題呢,其實很簡單,原理就是轉換下查詢思路,把想要的資料通過SQL語句一次性查詢出來,剩下的交由記憶體中處理,對此框架都是支援的,
即修改後如下:

-- 查詢客戶+訂單資訊
<resultMap type="Customers" id="customersResultMap" >
	<id column="customerId"/>
	<result property="customerName" column="customerName" javaType="string"/>
	<collection property="orders" ofType="Order">
		<id column="customerId"/>
		<result property="orderId" column="orderId" javaType="string"/>
	  <result property="num" column="num" javaType="string"/>
	  <result property="total" column="total" javaType="string"/>
	</collection>
</resultMap>
<select id="loadCustomers" statementType="PREPARED" parameterType="java.util.HashMap" resultMap="customersResultMap">
	select customerId, customerName 
	  from tb_customers cm
	  left join tb_order od 
	         on cm.customerId = od.customerId
</select>

如上,BOM模型不用變動,而且對資料庫只有一次性查詢

不知是能講清楚,至此結束,謝謝!