核心概念:QueryWrapper / LambdaQueryWrapper
MyBatis-Plus 提供了 QueryWrapper 這個強大的查詢構造器。它的 Lambda 版本 LambdaQueryWrapper 允許你直接使用實體類的 Lambda 表達式來指定字段,從而避免了手寫字符串字段名可能帶來的錯誤。
// 普通 QueryWrapper
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "張三").ge("age", 18);
// LambdaQueryWrapper (推薦)
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getName, "張三").ge(User::getAge, 18);
可以看到,LambdaQueryWrapper 更加類型安全,當你重構字段名時,編譯器會幫你發現問題。
場景一:一對一查詢 (One-to-One)
假設我們有兩個表:
user(用户表)user_profile(用户檔案表),它們通過user_id關聯。
需求: 查詢一個用户及其對應的檔案信息。
方法一:使用 XML/註解自定義 SQL (最常用)
這是最靈活、最直接的方式。你可以在 Mapper 接口中編寫自定義的 JOIN 查詢,然後用 MyBatis 的結果映射(ResultMap)來將查詢結果映射到一個自定義的 DTO(Data Transfer Object)中。
- 創建 DTO:創建一個用於接收聯合查詢結果的類。
// UserWithProfileDTO.java
@Data
public class UserWithProfileDTO {
private Long userId;
private String userName;
private Integer userAge;
private String profileAddress;
private String profilePhone;
// ... 其他字段
}
- 在 Mapper 接口中定義方法:
// UserMapper.java
public interface UserMapper extends BaseMapper<User> {
/**
* 根據用户ID查詢用户及其檔案
* @param userId 用户ID
* @return 用户與檔案的組合DTO
*/
UserWithProfileDTO getUserWithProfileById(Long userId);
}
- 在 XML 中編寫 SQL:
<!-- UserMapper.xml -->
<resultMap id="UserWithProfileResultMap" type="com.example.dto.UserWithProfileDTO">
<id property="userId" column="user_id"/>
<result property="userName" column="name"/>
<result property="userAge" column="age"/>
<result property="profileAddress" column="address"/>
<result property="profilePhone" column="phone"/>
</resultMap>
<select id="getUserWithProfileById" resultMap="UserWithProfileResultMap">
SELECT
u.id as user_id,
u.name,
u.age,
up.address,
uP.phone
FROM
user u
LEFT JOIN
user_profile up ON u.id = up.user_id
WHERE
u.id = #{userId}
</select>
注意: 這裏的 SQL 是手寫的,但結果映射可以通過 XML 或註解完成。Lambda 在這個場景中主要用於單表查詢,多表的 JOIN 條件和字段名目前仍需手動編寫。
方法二:使用 MyBatis-Plus 的 @Sql 註解 (適用於簡單SQL)
如果 SQL 比較簡單,你也可以直接在 Mapper 接口的方法上使用 @Sql 註解。
// UserMapper.java
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT u.id, u.name, up.address " +
"FROM user u " +
"LEFT JOIN user_profile up ON u.id = up.user_id " +
"WHERE u.id = #{userId}")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "name", column = "name"),
@Result(property = "profile.address", column = "address") // 如果User實體包含Profile對象
})
User getUserWithProfileSimple(@Param("userId") Long userId);
}
這種方法不需要額外的 XML 文件,但可讀性稍差。
場景二:一對多查詢 (One-to-Many)
假設我們有:
order(訂單表)order_item(訂單項表),一個訂單對應多個訂單項。
需求: 查詢一個訂單及其包含的所有訂單項。
推薦方法:使用 XML 和 resultMap 的 <collection>
這是處理一對多關係的標準做法。
- 確保你的實體類結構正確:
// Order.java
@Data
public class Order {
private Long id;
private Long userId;
private LocalDateTime createTime;
// 一個訂單包含多個訂單項
private List<OrderItem> orderItems;
}
// OrderItem.java
@Data
public class OrderItem {
private Long id;
private Long orderId;
private String productName;
private Integer quantity;
}
- 在 XML 中定義
resultMap:
<!-- OrderMapper.xml -->
<resultMap id="OrderWithItemsResultMap" type="com.example.entity.Order">
<id property="id" column="order_id"/>
<result property="userId" column="user_id"/>
<result property="createTime" column="create_time"/>
<!-- 配置一對多關係 -->
<collection property="orderItems" ofType="com.example.entity.OrderItem">
<id property="id" column="item_id"/>
<result property="orderId" column="order_id"/>
<result property="productName" column="product_name"/>
<result property="quantity" column="quantity"/>
</collection>
</resultMap>
<select id="getOrderWithItemsById" resultMap="OrderWithItemsResultMap">
SELECT
o.id as order_id,
o.user_id,
o.create_time,
oi.id as item_id,
oi.product_name,
oi.quantity
FROM
`order` o
LEFT JOIN
order_item oi ON o.id = oi.order_id
WHERE
o.id = #{orderId}
</select>
- 在 Mapper 接口中定義方法:
// OrderMapper.java
public interface OrderMapper extends BaseMapper<Order> {
Order getOrderWithItemsById(Long orderId);
}
總結與最佳實踐
- MyBatis-Plus 的定位:MyBatis-Plus 主要是為了簡化單表的 CRUD 操作。對於複雜的多表查詢,它的核心價值在於提供了
LambdaQueryWrapper等工具來優化單表條件的構建。 - 多表查詢的核心:多表查詢的
JOIN邏輯和結果映射(ResultMap)仍然需要你手動編寫 SQL 來實現,這部分是 MyBatis 的核心功能,MyBatis-Plus 並未試圖完全封裝它。 - 推薦方案:
- 對於大部分項目:使用 XML 或註解編寫自定義 SQL + ResultMap 是最穩定、最靈活、可讀性也最好的方案。
- 追求極致簡潔:如果你的多表查詢非常簡單,且不想維護 XML 文件,可以使用
@Select註解。 - 複雜動態查詢:如果
JOIN的表、條件都需要動態決定,可以結合QueryWrapper和自定義 SQL 片段來構建動態 SQL,但這會比較複雜。
- Lambda 的應用:在多表查詢中,Lambda 主要用於單表內的條件構造。例如,如果你需要根據訂單狀態和用户名稱來查詢,你可以先用
LambdaQueryWrapper構建好user表和order表的查詢條件,再將這些條件拼接到最終的JOINSQL 中(通常需要手動拼接或使用 MyBatis 的動態 SQL 標籤)。