Smart Mybatis 中的联表查询功能
鲜有原生支持联表的框架, 在Smart Mybatis中, 增加了联表查询的功能:
联表查询示例
假设我们有两个实体类 User 和 Order, 其中 User 和 Order 之间存在一对多的关系。
我们要做的事情很简单: 查询订单时, 把对应用户的名字一起查出来, 并填充到 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关联UserOrderItem通过orderId关联OrderOrderItem通过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。 - 如果多表之间存在一对多, 结果行数可能会被放大, 注意业务层去重或聚合。

浙公网安备 33010602011771号