postgresql QueryWrapper left join
如果你必须用 PostgreSQL 的LATERAL优化(取最新附件):请使用方案一 (XML)。这是最稳妥、性能最好且能完整利用 PG 特性的方式。如果只是普通联查且不想写 XML:可以尝试方案二 (。不要试图用原生强行做复杂联查,那是它的设计盲区。
原生的 MyBatis-Plus QueryWrapper 不支持 LEFT JOIN。
QueryWrapper 的设计初衷是用于单表的 CRUD 操作。它生成的 SQL 结构固定为 SELECT ... FROM table WHERE ...,无法直接生成 JOIN 子句。
但是,如果你需要在 MyBatis-Plus 中实现联查(特别是结合你上一个问题中的 PostgreSQL LATERAL 语法),有以下 3 种解决方案:
方案一:使用 XML 映射文件(最推荐,功能最强)
如果你需要使用 PostgreSQL 特有的 LEFT JOIN LATERAL 语法,或者联查逻辑比较复杂,XML 是最佳选择。它保留了 MyBatis-Plus 的分页和基础功能,同时允许你编写任意复杂的 SQL。
1. Mapper 接口
@Mapper
public interface ApplicationMapper extends BaseMapper<Application> {
// 自定义方法,返回 DTO 而不是 Entity
List<AppDetailDTO> selectAppWithAttachment(IPage page, @Param("ew") Wrapper<Application> wrapper);
}
2. XML 文件 (ApplicationMapper.xml) 在这里你可以直接使用之前讨论的 LATERAL 语法,并动态拼接 QueryWrapper 生成的 WHERE 条件。
<mapper namespace="com.example.mapper.ApplicationMapper">
<select id="selectAppWithAttachment" resultType="com.example.dto.AppDetailDTO">
SELECT
a.id, a.title,
att.file_url AS attachmentUrl
FROM applications a
<!-- 这里可以自由写复杂的 PG 语法 -->
LEFT JOIN LATERAL (
SELECT file_url
FROM attachments
WHERE app_id = a.id
ORDER BY created_at DESC
LIMIT 1
) att ON true
<!-- 动态拼接 QueryWrapper 的 WHERE 条件 -->
${ew.customSqlSegment}
</select>
</mapper>
3. 调用
Page<AppDetailDTO> page = new Page<>(1, 10);
QueryWrapper<Application> wrapper = new QueryWrapper<>();
wrapper.eq("a.user_id", 123); // 注意:XML 中表别名要对应
// 调用自定义方法
applicationMapper.selectAppWithAttachment(page, wrapper);
- 优点:完全控制 SQL,支持 PG 特性,性能最好。
- 缺点:需要写 XML 文件。
方案二:使用增强插件 mybatis-plus-join (最像 Wrapper)
社区有一个非常流行的插件叫 mybatis-plus-join (MpJ),它在 QueryWrapper 的基础上扩展了联查功能。
1. 引入依赖
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join</artifactId>
<version>1.4.0</version> <!-- 请检查最新版本 -->
</dependency>
2. 代码使用
// 使用 MpJoinWrapper 代替 QueryWrapper
MpJoinWrapper<Application> wrapper = new MpJoinWrapper<>();
wrapper.select(Application::getId, Application::getTitle)
.select(Attachment::getFileUrl)
.leftJoin(Attachment.class, Attachment::getAppId, Application::getId)
// 注意:MpJ 默认可能不支持 LIMIT 1 的子查询逻辑,
// 复杂的一对一取最新记录,可能还是需要配合 apply 或 XML
.orderByDesc(Attachment::getCreatedAt);
// 查询
List<AppDetailDTO> list = applicationMapper.selectJoinList(page, wrapper, AppDetailDTO.class);
- 优点:链式调用,无需 XML,开发快。
- 缺点:对于“一对多取一条”(如取最新附件)这种逻辑,原生 MpJ 处理起来可能不如
LATERAL直观,可能需要配合apply。
方案三:使用 apply() 方法硬拼 SQL (不推荐,仅限简单场景)
原生 QueryWrapper 提供了 apply() 方法,可以注入原生 SQL 片段。
QueryWrapper<Application> wrapper = new QueryWrapper<>();
wrapper.select("a.*", "att.file_url")
// 硬编码 JOIN 语句
.apply("LEFT JOIN attachments att ON a.id = att.app_id")
.eq("a.user_id", 123);
- 严重缺点:
- 无法处理一对多取一条:直接
LEFT JOIN会导致申请记录膨胀(一条申请对应多条附件,结果集会有多行)。 - SQL 注入风险:
apply中的参数如果拼接不当,容易出问题。 - 维护性差:SQL 散落在 Java 代码中。
- 不支持 PG
LATERAL:很难在apply里优雅地写子查询逻辑。
- 无法处理一对多取一条:直接
关键提示:结果映射 (DTO/VO)
无论用哪种方式,联查后的结果不能直接映射到 Application 实体类上,因为实体类里没有 attachment_url 字段,且一对多会导致数据重复。
你需要创建一个 DTO (Data Transfer Object) 或 VO (View Object):
@Data
public class AppDetailDTO {
private Long id;
private String title;
private String attachmentUrl; // 附件字段
}
总结建议
- 如果你必须用 PostgreSQL 的
LATERAL优化(取最新附件):请使用 方案一 (XML)。这是最稳妥、性能最好且能完整利用 PG 特性的方式。 - 如果只是普通联查且不想写 XML:可以尝试 方案二 (
mybatis-plus-join)。 - 不要试图用原生
QueryWrapper强行做复杂联查,那是它的设计盲区。
更多推荐
所有评论(0)