Hibernate CollectionOfElements EAGER 获取重复元素

Posted

技术标签:

【中文标题】Hibernate CollectionOfElements EAGER 获取重复元素【英文标题】:Hibernate CollectionOfElements EAGER fetch duplicates elements 【发布时间】:2010-11-08 18:04:48 【问题描述】:

我有一个名为 SynonymMapping 的类,它有一组映射为 CollectionOfElements 的值

@Entity(name = "synonymmapping")
public class SynonymMapping 

    @Id private String keyId;

    //@CollectionOfElements(fetch = FetchType.EAGER)
    @CollectionOfElements
    @JoinTable(name="synonymmappingvalues", joinColumns=@JoinColumn(name="keyId"))
    @Column(name="value", nullable=false)
    @Sort(type=SortType.NATURAL)
    private SortedSet<String> values;

    public SynonymMapping() 
        values = new TreeSet<String>();
    

    public SynonymMapping(String key, SortedSet<String> values) 
        this();
        this.keyId = key;
        this.values = values;
    

    public String getKeyId() 
        return keyId;
    

    public Set<String> getValues() 
        return values;
    

我有一个测试,我将两个 SynonymMapping 对象存储到数据库,然后要求数据库返回所有保存的 SynonymMapping 对象,期望接收我存储的两个对象。

当我将值的映射更改为渴望(如代码中注释掉的行所示)并再次运行测试时,我收到了四个匹配项。

我已经在运行之间清除了数据库,我可以在急切和懒惰之间重复这个问题。

我认为这与 hibernate 在下面创建的连接有关,但我在网上找不到明确的答案。

谁能告诉我为什么急切的提取会复制对象?

谢谢。

【问题讨论】:

每个出现异常“找到多个具有给定标识符的行”的人都应该知道这一点。它真的节省了很多时间,不知道到底出了什么问题。见@user176668 回答!! 【参考方案1】:

在映射中强制执行预先获取通常不是一个好主意 - 最好在适当的查询中指定预先连接(除非您 100% 确定在任何情况下您的对象都没有意义/无效没有填充该集合)。

您得到重复的原因是因为 Hibernate 在内部连接了您的根表和集合表。请注意,它们确实是重复的,例如对于每个具有 3 个集合元素的 2 个 SynonymMapping,您将获得 6 个结果 (2x3),每个 SynonymMapping 实体的 3 个副本。因此,最简单的解决方法是将结果包装在 Set 中,从而确保它们是唯一的。

【讨论】:

但是为什么 Hibernate 不能过滤掉这些,我不明白为什么你会想要这样。 我可以确认它有效。我只是在一般情况下使用 Collection,这是一个错误。 注意:包装在 Set 中会导致对结果进行的任何分页都不正确 一般来说使用Set是个好习惯吗?它解决了这个“重复”问题,所以我会将它用于我的所有收藏。它有什么缺点吗?【参考方案2】:

您可以按如下方式使用 SELECT DISTINCT(休眠查询语言)子句

SELECT DISTINCT synonym FROM SynonymMapping synonym LEFT JOIN FETCH synonym.values

DISTINCT 子句删除 Hibernate 中的重复引用。

虽然组件和值类型集合的生命周期都绑定到所属实体类,但您应该在 select 子句中声明它们以便检索它们。 (LEFT JOIN FETCH synonym.values)

ChssPly76 的回答是另一种方法,但不要忘记根据Set语义覆盖equals和hashcode方法

问候,

【讨论】:

我想知道更多关于为什么会发生这种情况以及为什么设计决策是为了让 hibernate 做出这样的响应而不是解决它的不同方法。【参考方案3】:

我遇到了同样的问题 - 当您为 @CollectionOfElements 设置 FetchType.EAGER 时,Hibernate 会尝试一次性获取所有内容,即对链接到“主”对象的每个元素条目使用一个查询。如果您将@Fetch (FetchMode.SELECT) 注释添加到您的集合中,则可以以 N+1 查询为代价成功解决此问题。 在我的例子中,我想要一个 MediaObject 实体及其元数据项(视频编解码器、音频编解码器、大小等)的集合。 metadataItems 集合的映射如下所示:

@CollectionOfElements (targetElement = String.class, fetch = FetchType.EAGER) @JoinTable(name = "mo_metadata_item", joinColumns = @JoinColumn(name = "media_object_id")) @MapKey(columns = @Column(name = "name")) @Column(名称=“值”) @Fetch (FetchMode.SELECT) 私有 Map metadataItems = new HashMap();

【讨论】:

我喜欢这个解决方案,因为我们不需要用 Set 包装结果来获得唯一性。 会不会也是EHcache等缓存造成的? 这个解决方案效果很好,但是,它依赖于实现。 谢谢。我已经做了很多关于它的搜索。每个人都说要使用 Set。 花了很长时间来调试它,甚至更长时间才能找到不是实现集合的教程的答案。感谢分享。【参考方案4】:

我遇到过这个问题,我用

解决了这个问题

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

这将清除由于对子表进行连接而导致的重复项。

【讨论】:

【参考方案5】:

而不是FetchMode.SELECT 使用 N+1 查询,使用BatchSize e.q 更好。 @BatchSize(size = 200).

DISTINCTCriteria.DISTINCT_ROOT_ENTITY 无济于事,如果您必须获取超过 1 个关联。对于这种情况,请参阅其他解决方案:https://***.com/a/46013654/548473

【讨论】:

【参考方案6】:

我通过简单的添加实现了它

session.createCriteria(ModelClass.class).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

这有助于删除重复项。

【讨论】:

以上是关于Hibernate CollectionOfElements EAGER 获取重复元素的主要内容,如果未能解决你的问题,请参考以下文章

Spring和Hibernate的注解整合 hibernate3和hibernate4/5的区别

hibernate.merge()方法怎么用

hibernate 异常 怎么解决

Hibernate之Hibernate环境配置

(转)Hibernate框架基础——Hibernate API及Hibernate主配置文件

Hibernate基础学习—Hibernate相关API介绍