hibernate JPARepository返回重复的子实体

ggazkfy8  于 2023-08-06  发布在  其他
关注(0)|答案(1)|浏览(114)

我正在开发我的应用程序中的聊天功能。当我试图从数据库中检索Chat对象时,出现了问题,所有消息都被检索了两次。

项目结构

以下是简化的结构:
x1c 0d1x的数据
这是实体(简化):

@Entity
public class Chat {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany
    @JoinTable(
            name = "user_chat",
            joinColumns = @JoinColumn(name = "chat"),
            inverseJoinColumns = @JoinColumn(name = "user")
    )
    private Set<User> users = new HashSet<>();

    @OneToMany(mappedBy = "chat", cascade = CascadeType.ALL)
    private List<Message> messages = new ArrayList<>();

    public void addMessage(Message message){
        this.messages.add(message);
    }
}

@Entity
public class Message {
    private @Id @GeneratedValue Long id;

    @ManyToOne
    @JoinColumn(name="senderId", nullable=false)
    private User sender;

    @Column(name="value")
    private String value;

    @ManyToOne()
    @JoinColumn(name = "chat")
    private Chat chat;
}

@Entity
public class User implements UserDetails {
    private @Id
    @GeneratedValue
    Long id;

    @OneToMany(mappedBy = "sender", cascade = CascadeType.ALL)
    private List<Message> messagesSent;

    @ManyToMany(mappedBy = "users")
    private Set<Chat> chats = new HashSet<>();
}

字符串
这就是失败的仓库:

public interface ChatRepository extends JpaRepository<Chat,Long> {
    @Query("SELECT c FROM Chat c JOIN c.users u WHERE u.id IN :usersIDs GROUP BY c HAVING COUNT(DISTINCT u) = :numberOfUsers")
    Optional<Chat> getChat(@Param("usersIDs") List<Long> usersIDs, int numberOfUsers);
}

问题

在这里,您可以看到如何检索对象的示例:



可以看到,每个Message对象都是重复的
我想问题是
在研究和执行对数据库的原始SQL查询时,我假设问题可能是这样的:Hibernate是这样做的查询:加入聊天和用户之间,然后加入结果和消息之间。这会使消息重复,因为它将每条消息与Chat-User 1和Chat-User 2行连接起来。就这样:

SELECT message.id as messageID, chat.id as chatID, user.id as userID FROM chat JOIN user JOIN message;


我一直在努力

作为对我之前解释的假设的回应,我尝试以这种方式更改存储库查询:

public interface ChatRepository extends JpaRepository<Chat,Long> {
        @Query("SELECT c FROM Chat c JOIN c.users u LEFT JOIN c.messages m ON m.sender = u AND m.chat = c WHERE u.id IN :usersIDs GROUP BY c HAVING COUNT(DISTINCT u) = :numberOfUsers")
        Optional<Chat> getChat(@Param("usersIDs") List<Long> usersIDs, int numberOfUsers);
    }


查询明细:
SELECT c FROM Chat c JOIN c.users u聊天和用户已加入
LEFT JOIN c.messages m ON m.sender = u AND m.chat = c结果和消息之间的连接
WHERE u.id IN :usersIDs筛选刚刚请求的用户
GROUP BY c HAVING COUNT(DISTINCT u) = :numberOfUsers")检索所有用户都请求的聊天
正如您所看到的,差异在于ON m.sender = u AND m.chat = c。这样,Messages也必须加入User,如下面的查询:

SELECT message.id as messageID, chat.id as chatID, user.id as userID FROM chat JOIN user JOIN message ON message.sender_id = user.id and message.chat = chat.id;



而JOIN之前的LEFT则是为了获取没有发消息到聊天中但已经是成员的用户。
但是,此查询并不能解决问题。返回带有复制消息的聊天。
这是我尝试过的其他事情的列表:

  • private List<Message> messages = new ArrayList<>();之前添加@Fetch (FetchMode.SELECT)
  • 在查询前使用@Transactional
  • 覆盖Message中的equals(Object o)

下面是message.equals()方法的样子:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Message message = (Message) o;
    return Objects.equals(id, message.id) && Objects.equals(sender, message.sender) && Objects.equals(value, message.value) && Objects.equals(chat, message.chat);
}

@Override
public int hashCode() {
    return Objects.hash(id, sender, value, chat);
}

我看过的其他SO帖子

JPA - EntityManager find method returns duplicates
Multiple fetches with EAGER type in Hibernate with JPA

dgiusagp

dgiusagp1#

当你的查询执行时,你的模型将总是返回一个由3个表组成的笛卡尔图。
我相信在这种情况下,你应该做的是在你的投影中实现DISTINCT子句,这样Hibernate就能够比较对象并将每个不同消息中的一个添加到消息列表中。

public interface ChatRepository extends JpaRepository<Chat,Long> {
        @Query("SELECT DISTINCT c FROM Chat c INNER JOIN FETCH c.users u LEFT JOIN FETCH c.messages m WHERE u.id IN :usersIDs GROUP BY c HAVING COUNT(DISTINCT u) = :numberOfUsers")
        Optional<Chat> getChat(@Param("usersIDs") List<Long> usersIDs, int numberOfUsers);
    }

字符串
我还在join中添加了FETCH,以将消息检索到单个快照中,但这也会使查询更大,并添加另一个user表示例来检索消息的用户,而不是重用聊天的用户。
除了distinct对数据库查询没有影响之外,Hibernate还知道结果必须经过一个归约过程来消除重复。
另外,这篇文章可能会有所帮助:https://vladmihalcea.com/jpql-distinct-jpa-hibernate/

相关问题