MyBatis一对多查询方式
在 MyBatis 中,一对多查询是指一个实体对象(如 Order
)关联多个子对象(如 OrderItem
)。这种关系在数据库中通常通过外键实现,而在 MyBatis 中可以通过 resultMap
的嵌套集合(<collection>
)来处理。
以下是对 MyBatis 中一对多查询的详细介绍,包括实现步骤和示例代码。
1. 一对多关系的数据表设计
假设我们有两个表:
Orders
表:存储订单信息。OrderItems
表:存储订单项信息,每个订单项属于一个订单。
表结构如下:
CREATE TABLE Orders (
order_id INT PRIMARY KEY,
order_number VARCHAR(50),
customer_id INT,
order_date DATE
);
CREATE TABLE OrderItems (
item_id INT PRIMARY KEY,
order_id INT,
product_name VARCHAR(100),
quantity INT,
price DECIMAL(10, 2),
FOREIGN KEY (order_id) REFERENCES Orders(order_id)
);
- 一个订单(
Orders
)可以包含多个订单项(OrderItems
)。 OrderItems
表中的order_id
是外键,指向Orders
表的order_id
。
2. 实体类设计
在 Java 中,我们需要定义两个实体类来表示这种一对多关系。
Order
类
public class Order {
private int orderId;
private String orderNumber;
private int customerId;
private Date orderDate;
private List<OrderItem> orderItems; // 一对多关系
// Getters and Setters
}
OrderItem
类
public class OrderItem {
private int itemId;
private int orderId;
private String productName;
private int quantity;
private BigDecimal price;
// Getters and Setters
}
3. MyBatis 实现一对多查询
在 MyBatis 中,一对多查询通常通过 resultMap
的 <collection>
标签来实现。以下是具体步骤。
3.1 定义 resultMap
在 MyBatis 的 Mapper XML 文件中,定义一个 resultMap
,使用 <collection>
标签来表示一对多关系。
<resultMap id="OrderResultMap" type="Order">
<!-- 映射 Order 表的基本字段 -->
<id property="orderId" column="order_id"/>
<result property="orderNumber" column="order_number"/>
<result property="customerId" column="customer_id"/>
<result property="orderDate" column="order_date"/>
<!-- 一对多关系:Order 包含多个 OrderItem -->
<collection property="orderItems" ofType="OrderItem">
<id property="itemId" column="item_id"/>
<result property="orderId" column="order_id"/>
<result property="productName" column="product_name"/>
<result property="quantity" column="quantity"/>
<result property="price" column="price"/>
</collection>
</resultMap>
property="orderItems"
:对应Order
类中的orderItems
属性。ofType="OrderItem"
:表示集合中的元素类型是OrderItem
。
3.2 编写 SQL 查询
使用 LEFT JOIN
将 Orders
表和 OrderItems
表连接起来,确保即使某个订单没有订单项,也能查询到订单信息。
<select id="getOrderWithItems" resultMap="OrderResultMap">
SELECT
o.order_id, o.order_number, o.customer_id, o.order_date,
i.item_id, i.product_name, i.quantity, i.price
FROM Orders o
LEFT JOIN OrderItems i ON o.order_id = i.order_id
WHERE o.order_id = #{orderId}
</select>
- 使用
LEFT JOIN
是为了确保即使某个订单没有订单项,也能返回订单信息。 #{orderId}
是传入的参数。
3.3 Java 接口
在 Java 接口中定义方法:
public interface OrderMapper {
Order getOrderWithItems(@Param("orderId") int orderId);
}
4. 查询结果
假设数据库中有以下数据:
Orders
表:
order_id | order_number | customer_id | order_date |
---|---|---|---|
1 | ORDER001 | 101 | 2023-10-01 |
2 | ORDER002 | 102 | 2023-10-02 |
OrderItems
表:
item_id | order_id | product_name | quantity | price |
---|---|---|---|---|
1 | 1 | Product A | 2 | 100.00 |
2 | 1 | Product B | 1 | 50.00 |
3 | 2 | Product C | 3 | 200.00 |
调用 getOrderWithItems(1)
后,返回的 Order
对象如下:
Order {
orderId: 1,
orderNumber: "ORDER001",
customerId: 101,
orderDate: "2023-10-01",
orderItems: [
OrderItem {
itemId: 1,
orderId: 1,
productName: "Product A",
quantity: 2,
price: 100.00
},
OrderItem {
itemId: 2,
orderId: 1,
productName: "Product B",
quantity: 1,
price: 50.00
}
]
}
5. 一对多查询的优化
如果查询结果较大,可能会产生性能问题(如 N+1 查询问题)。可以通过以下方式优化:
5.1 使用 LEFT JOIN
一次性加载
如上面的示例,使用 LEFT JOIN
一次性加载所有数据,避免多次查询。
5.2 分步查询
如果数据量较大,可以使用分步查询(嵌套查询):
<resultMap id="OrderResultMap" type="Order">
<id property="orderId" column="order_id"/>
<result property="orderNumber" column="order_number"/>
<result property="customerId" column="customer_id"/>
<result property="orderDate" column="order_date"/>
<!-- 分步查询:通过 order_id 查询 OrderItems -->
<collection property="orderItems" ofType="OrderItem" select="selectOrderItemsByOrderId" column="order_id"/>
</resultMap>
<select id="selectOrderItemsByOrderId" resultType="OrderItem">
SELECT item_id, order_id, product_name, quantity, price
FROM OrderItems
WHERE order_id = #{orderId}
</select>
<select id="getOrderWithItems" resultMap="OrderResultMap">
SELECT order_id, order_number, customer_id, order_date
FROM Orders
WHERE order_id = #{orderId}
</select>
select="selectOrderItemsByOrderId"
:指定嵌套查询的方法。column="order_id"
:将当前查询的order_id
作为参数传递给嵌套查询。
6. 总结
- 一对多查询在 MyBatis 中通过
<collection>
标签实现。 - 可以使用
LEFT JOIN
一次性加载数据,也可以使用分步查询优化性能。 - 确保实体类和数据库表的结构正确映射。
- 通过
resultMap
可以灵活地处理复杂的对象关系。
通过以上方法,你可以在 MyBatis 中轻松实现一对多查询,并灵活应对不同的业务需求。