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

如何用JPA替代含UNION的SQL查询获取聊天关联用户?

解决JPA中获取聊天唯一收发方的问题(替代UNION实现)

问题背景

开发聊天应用时,需要从数据库中获取当前登录用户的所有唯一聊天对象。现有Message实体模型如下:

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;

@NotNull
private String Content;

@Temporal(TemporalType.TIMESTAMP)
private Date date = new Date();

@ManyToOne
@JoinColumn(name = "sender_id")
private User sender;

@ManyToOne
@JoinColumn(name = "receiver_id")
private User receiver;

数据库中数据示例(当前用户为A):

senderreceiver
AB
BA
AC
DA

期望结果:B、C、D,对应SQL实现为:

select distinct receiver_id from message where sender_id =4 union 
select distinct sender_id from message as m where receiver_id = 4

但JPA标准JPQL不支持UNION,可通过以下几种方式解决:

解决方案1:两次查询+集合合并去重

通过两次JPA查询分别获取用户作为发送方的接收者、作为接收方的发送者,再用Java集合操作去重:

在MessageRepository中定义两个查询方法:

public interface MessageRepository extends JpaRepository<Message, Integer> {
    // 获取当前用户作为发送方时的所有接收者
    List<User> findDistinctReceiverBySenderId(Integer userId);
    // 获取当前用户作为接收方时的所有发送者
    List<User> findDistinctSenderByReceiverId(Integer userId);
}

业务代码中合并去重:

// 当前登录用户ID
Integer currentUserId = 4;
List<User> receivers = messageRepository.findDistinctReceiverBySenderId(currentUserId);
List<User> senders = messageRepository.findDistinctSenderByReceiverId(currentUserId);

// 合并两个集合并去重
Set<User> uniqueChatUsers = new HashSet<>();
uniqueChatUsers.addAll(receivers);
uniqueChatUsers.addAll(senders);

// 转换为List(如果需要)
List<User> result = new ArrayList<>(uniqueChatUsers);

解决方案2:JPQL子查询实现

利用JPQL的IN子查询合并两个条件,通过DISTINCT去重:

在MessageRepository中添加自定义JPQL查询:

public interface MessageRepository extends JpaRepository<Message, Integer> {
    @Query("SELECT DISTINCT u FROM User u WHERE " +
           "u.id IN (SELECT m.receiver.id FROM Message m WHERE m.sender.id = :userId) " +
           "OR u.id IN (SELECT m.sender.id FROM Message m WHERE m.receiver.id = :userId)")
    List<User> findUniqueChatUsers(@Param("userId") Integer userId);
}

直接调用该方法即可得到去重后的聊天对象列表。

解决方案3:使用原生SQL查询

JPA支持执行原生SQL,直接复用原有的UNION查询:

在MessageRepository中添加原生SQL查询:

public interface MessageRepository extends JpaRepository<Message, Integer> {
    // 获取唯一聊天对象ID
    @Query(value = "select distinct receiver_id as userId from message where sender_id = :userId union " +
                   "select distinct sender_id as userId from message where receiver_id = :userId",
           nativeQuery = true)
    List<Integer> findUniqueChatUserIds(@Param("userId") Integer userId);

    // 直接获取User对象
    @Query(value = "SELECT DISTINCT u.* FROM user u " +
                   "JOIN (select distinct receiver_id as id from message where sender_id = :userId " +
                   "union select distinct sender_id as id from message where receiver_id = :userId) t " +
                   "ON u.id = t.id",
           nativeQuery = true)
    List<User> findUniqueChatUsers(@Param("userId") Integer userId);
}

这种方式完全复用原SQL逻辑,适合复杂查询场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 10:45:34