You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Data JPA:如何获取每个发送者发送给接收者的首条消息

如何获取每个发送者给指定接收者的第一条消息?

针对你提出的需求——获取所有发送者给指定接收者的消息,且仅保留每个发送者最早的那条,我结合你的Message实体类给出几种可行的实现方案:

前提说明

首先假设你的DefaultEntity父类包含了创建时间字段(比如createdDate),这是按时间排序取第一条的核心依据,如果你的字段名不同,记得对应调整下面代码里的字段名称。


方案一:使用JPQL自定义查询

这是最灵活直接的方式,通过JPQL语句精准筛选目标数据:

@Repository
public interface MessageRepository extends JpaRepository<Message, Long> {

    @Query("SELECT m FROM Message m " +
           "WHERE m.receiver = :receiver " +
           "AND m.createdDate = (" +
               "SELECT MIN(m2.createdDate) FROM Message m2 " +
               "WHERE m2.sender = m.sender AND m2.receiver = :receiver" +
           ")")
    List<Message> findFirstMessageFromEachSenderToReceiver(@Param("receiver") User receiver);
}

逻辑拆解:

  • 外层查询先筛选出接收者为指定用户的所有消息
  • 子查询针对每个发送者,找出其给该接收者发送消息的最早创建时间
  • 最终只保留那些创建时间等于对应发送者最早时间的消息,也就是每个发送者的第一条消息

方案二:Spring Data JPA分组+分步查询

如果想借助Spring Data的方法特性,可以先获取每个发送者最早消息的ID,再查询完整实体:

@Repository
public interface MessageRepository extends JpaRepository<Message, Long> {

    // 第一步:获取每个发送者给指定接收者的最早消息ID
    @Query("SELECT m.id FROM Message m " +
           "WHERE m.receiver = :receiver " +
           "GROUP BY m.sender " +
           "HAVING m.createdDate = MIN(m.createdDate)")
    List<Long> findFirstMessageIdsFromEachSenderToReceiver(@Param("receiver") User receiver);

    // 第二步:根据ID列表查询完整消息实体
    List<Message> findByIdIn(List<Long> ids);
}

在业务层调用时组合这两个方法即可:

List<Long> firstMessageIds = messageRepository.findFirstMessageIdsFromEachSenderToReceiver(targetUser);
List<Message> firstMessages = messageRepository.findByIdIn(firstMessageIds);

方案三:原生SQL窗口函数查询(适合复杂场景)

如果你的数据库支持窗口函数(比如MySQL 8.0+、PostgreSQL),可以用ROW_NUMBER()实现更高效的分组排序:

@Repository
public interface MessageRepository extends JpaRepository<Message, Long> {

    @Query(value = "SELECT * FROM (" +
                   "SELECT *, " +
                   "ROW_NUMBER() OVER (PARTITION BY sender ORDER BY created_date ASC) AS rn " +
                   "FROM message " +
                   "WHERE receiver_id = :receiverId" +
                   ") AS temp " +
                   "WHERE rn = 1",
           nativeQuery = true)
    List<Message> findFirstMessagesFromEachSender(@Param("receiverId") Long receiverId);
}

逻辑拆解:

  • 用ROW_NUMBER()窗口函数按发送者分组,每组内按创建时间升序给消息编号
  • 只保留编号为1的记录,也就是每个发送者的第一条消息
  • 这里直接传入接收者ID而非User实体,查询效率会更高,你可以根据业务场景调整参数类型

注意事项

  • 确保createdDate字段在实体类中正确映射到数据库表的对应字段
  • 如果出现多个消息创建时间完全相同的极端情况,建议结合ID排序(比如用MIN(id)替代MIN(createdDate))来确保只返回一条
  • 测试时可以手动插入几条不同发送者的消息,验证查询结果是否符合预期

内容的提问来源于stack exchange,提问作者Thanus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:06:14