为什么休眠 PersistentSet.contains() 这么慢? (与 java.util.HashSet 相比)

Why hibernate PersistentSet.contains() is so slow? (compared to java.util.HashSet)

我正在开发一个用例,在该用例中,我获得了一组 ID(名为 group),并且需要验证其中哪些 ID 位于另一个集合(名为 projectDevicesIds)中,并且哪些没有。请注意,最后一个集合是从数据库中获取的 PersistentSet。代码非常简单如下:

Collection<String> inside = new HashSet<>();
Collection<String> notInside = new HashSet<>();
group.forEach(id -> {
        if (projectDevicesIds.contains(id)) inside.add(id);
        else notInside.add(id);
    });

到目前为止一切顺利,问题是当 projectDevicesIds (hibernate PersistentSet) 的大小为 100 000 且 group 包含 1000 个 id 时,此代码平均需要 200 毫秒才能 运行.当我做同样的测试但不是使用 PersistentSet 而是使用 HashSet 时,它只需要 1 毫秒!即使测试在专业上不准确,这种差异也是疯狂的并且会损害我的用例性能。在休眠中 official docs 他们说 PersistentSet 在内部使用 HashSet,所以我期望的性能大致相同。

有人可以向我解释为什么 PersistentSet.contains() 与 HashSet 相比需要这么长时间吗?并以某种方式帮助我改进此用例性能?

PersistentSet 表示数据库上的关联。这意味着当你调用 contains 时,Hibernate ORM 需要首先刷新之前可能影响关联的操作,并最终从数据库中重新加载它。或者,如果关联已延迟加载,则可能需要重新加载它。

第一次加载集合后,性能差异应该不会那么高,但这实际上取决于您如何获得 projectDevicesIds

如果你启用日志,你应该看看 Hibernate ORM 在你调用 contains 方法时是否需要 运行 额外的查询。

@Davide 的回答解决了 forEach 时间问题(从 200 毫秒到 1 毫秒)但是 eager 抓取结果变慢了,总时间(eager fetch + forEach) 更高 ( > 250ms).

所以我想出了一个解决方法来强制使用 HashSet(而不是 PersistentSet)。我没有通过 Project POJO 获取设备,而是在 ProjectJpaRepositoty 中添加了自定义 SQL 查询以从特定项目获取设备 ID,但是 return class 是一个 HashSet:

public interface ProjectRepository extends JpaRepository<Project, String> {
            @Query(
                    value = "SELECT id FROM device WHERE project_id = :projectId",
                    nativeQuery = true
            )
            HashSet<String> getDevicesId(String projectId);
}

现在整体时间是我能得到的最低(~75ms),这是可以接受的。