Smart Mybatis 中的联表查询功能

鲜有原生支持联表的框架, 在Smart Mybatis中, 增加了联表查询的功能:

联表查询示例

假设我们有两个实体类 UserOrder, 其中 UserOrder 之间存在一对多的关系。
我们要做的事情很简单: 查询订单时, 把对应用户的名字一起查出来, 并填充到 Order.userName 字段。

1) 实体定义

User 实体:

@TableName("user")
public class User {
    @ID
    private Long id;
    private String name;
    // Getter和Setter方法
}

Order 实体:

@TableName("order")
public class Order {
    @ID
    private Long id;
    private Long userId;
    private String product;

    /**
     * 外键关联字段, 其中:
     * - `exist = false` 表示该字段不对应数据库表中的列, 仅用于关联查询时存储关联数据。
     * - `link = User.class` 指定关联的实体类为 `User`
     * - `linkField = "name"` 指定关联的字段为 `User` 实体类中的 `name` 字段。
     */
    @TableField(exist = false, link = User.class, linkField = "name")
    private String userName;
    // Getter和Setter方法
}

2) 最小可用查询: 查询订单并填充关联字段

我们希望在查询订单时, 同时获取订单对应的用户名字:

List<Order> orders = orderMapper.select(Where.where().leftJoin(
        User.class,
        "u",
        Where.where(User::getId).eq(Order::getUserId),
        Order::getUserName
));

SQL 对照示例

上面的写法大致等价于下面的 SQL (仅用于理解):

SELECT
  o.*, u.name AS user_name
FROM `order` o
LEFT JOIN `user` u ON u.id = o.user_id;

参数说明

  • User.class 指定要联表查询的实体类。
  • "u" 是联表查询中 User 表的别名。
  • Where.where(User::getId).eq(Order::getUserId) 定义联表条件, 表示 User.id = Order.userId
  • Order::getUserName 指定要填充的字段, 这里会把 User.name 写入 Order.userName

结果说明

查询结果 orders 将包含所有订单信息, 同时每个订单的 userName 字段将包含对应用户的名字。

逐步拆解: 读懂联表条件

将联表条件单独拿出来看, 会更容易理解:

Where joinOn = Where.where(User::getId).eq(Order::getUserId);

它表达的是 SQL 里的 User.id = Order.userId 条件, 也就是让订单和用户按外键关联。

把这个条件再放回联表查询中:

List<Order> orders = orderMapper.select(Where.where().leftJoin(
        User.class,
        "u",
        joinOn,
        Order::getUserName
));

变体示例: 只联表, 不填充字段

如果你只关心联表条件, 不需要填充任何字段, 可以省略最后的填充参数:

List<Order> orders = orderMapper.select(Where.where().leftJoin(
        User.class,
        "u",
        Where.where(User::getId).eq(Order::getUserId)
));

补充一个对应的 SQL 片段 (仅用于理解):

SELECT
  o.*
FROM `order` o
LEFT JOIN `user` u ON u.id = o.user_id;

变体示例: 填充多个字段

leftJoin 最后一个参数可以是多个字段, 你可以一次填充多个关联字段:

List<Order> orders = orderMapper.select(Where.where().leftJoin(
        User.class,
        "u",
        Where.where(User::getId).eq(Order::getUserId),
        Order::getUserName,
        Order::getUserEmail
));

这里假设 Order 里还定义了 userEmail 字段, 并在 @TableField 中配置了 linkField = "email"

补充一个对应的 SQL 片段 (仅用于理解):

SELECT
  o.*, u.name AS user_name, u.email AS user_email
FROM `order` o
LEFT JOIN `user` u ON u.id = o.user_id;

注意

  • Smart Mybatis 的联表查询使用 LEFT JOIN, 这意味着即使某些订单没有对应的用户信息, 这些订单仍会被包含在结果中。
  • 请确保关联实体类与字段配置正确, 避免联表结果异常。

联表查询的条件

1) 联表完成后再过滤

先把用户名字联出来, 再在主 Where 里做过滤:

Where joinOn = Where.where(User::getId).eq(Order::getUserId);

List<Order> orders = orderMapper.select(Where.where()
        .leftJoin(User.class, "u", joinOn, Order::getUserName)
        .like(Order::getUserName, "%张%")
);

SQL 对照 (仅用于理解):

SELECT
  o.*, u.name AS user_name
FROM `order` o
LEFT JOIN `user` u ON u.id = o.user_id
WHERE u.name LIKE '%张%';

含义: 过滤条件在 WHERE 中, 会把不满足条件的订单整体过滤掉。

2) 在联表过程中就过滤

把过滤条件放进联表条件里, 让 JOIN 时就只关联“包含张”的用户:

Where joinOn = Where.where(User::getId)
        .eq(Order::getUserId)
        .and(User::getName).like("%张%");

List<Order> orders = orderMapper.select(Where.where()
        .leftJoin(User.class, "u", joinOn, Order::getUserName)
);

SQL 对照 (仅用于理解):

SELECT
  o.*, u.name AS user_name
FROM `order` o
LEFT JOIN `user` u
  ON u.id = o.user_id AND u.name LIKE '%张%';

含义: 过滤条件在 JOIN ON 中, 不满足条件的订单仍保留, 但联表字段会是 null

如何选择

  • 需要过滤订单本身: 选择“联表完成后再过滤”。
  • 只想影响联表字段是否填充: 选择“在联表过程中就过滤”。

常见组合

实际业务中, 常常会同时存在“订单自身条件 + 用户条件”, 可以按语义拆开:

Where joinOn = Where.where(User::getId)
        .eq(Order::getUserId)
        .and(User::getName).like("%张%");

List<Order> orders = orderMapper.select(Where.where()
        .eq(Order::getStatus, "PAID")
        .leftJoin(User.class, "u", joinOn, Order::getUserName)
);

总结

  • 使用 LEFT JOIN 时, JOIN ON 的过滤不会影响主表的行数。
  • WHERE 过滤会影响最终结果集, 请确认这符合你的业务需求。

多表联表查询

关系设定

假设有以下关系:

  • Order 通过 userId 关联 User
  • OrderItem 通过 orderId 关联 Order
  • OrderItem 通过 productId 关联 Product

为了展示联查后的结果, 假设 Order 里还有一个 productName 字段用于填充商品名。

1) 拆解每个联表条件

Where joinUser = Where.where(User::getId).eq(Order::getUserId);
Where joinItem = Where.where(OrderItem::getOrderId).eq(Order::getId);
Where joinProduct = Where.where(Product::getId).eq(OrderItem::getProductId);

2) 串联多个 leftJoin

List<Order> orders = orderMapper.select(Where.where()
        .leftJoin(User.class, "u", joinUser, Order::getUserName)
        .leftJoin(OrderItem.class, "oi", joinItem)
        .leftJoin(Product.class, "p", joinProduct, Order::getProductName)
);

SQL 对照示例

SELECT
  o.*, u.name AS user_name, p.name AS product_name
FROM `order` o
LEFT JOIN `user` u ON u.id = o.user_id
LEFT JOIN `order_item` oi ON oi.order_id = o.id
LEFT JOIN `product` p ON p.id = oi.product_id;

常见注意点

  • 多表联查就是“多个 leftJoin 串起来”, 每个联表都有自己独立的 ON 条件。
  • 仍然是 LEFT JOIN, 关联不存在时对应的填充字段为 null
  • 如果多表之间存在一对多, 结果行数可能会被放大, 注意业务层去重或聚合。
posted @ 2026-02-26 16:12  Only丿阿海  阅读(5)  评论(0)    收藏  举报