2009-07-07 60 views
35

我有一个名为SynonymMapping类,其具有映射为CollectionOfElements休眠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创建的连接有关,但我无法在网上找到明确的答案。

任何人都可以告诉我为什么渴望获取是复制对象吗?

谢谢。

+0

每个人都有异常情况“找到多个具有给定标识符的行”应该知道这一点。它真的很省时,不知道到底是怎么回事。请参阅@ user176668答案! – 2016-05-27 10:31:44

回答

27

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

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

+34

但是为什么Hibernate不能过滤掉这些,我看不出为什么你会这样想。 – 2012-02-28 17:20:43

+0

我可以证实它的作品。我一般只使用Collection ,这是一个错误。 – 2015-06-22 08:39:14

2

可以按如下步骤

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

DISTINCT子句中使用一个SELECT DISTINCT(Hibernate查询语言)条款删除在Hibernate中重复引用。

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

ChssPly76的答案是另一种方法,但不忘记重写equals并根据hashCode方法设置语义

问候,

+3

我很想知道为什么发生这种情况,为什么设计决定是为了让hibernate响应这种情况而不是绕过它的不同方式。 – Rachel 2009-07-20 12:45:49

62

我走进了同样的问题 - 当你为@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 (name = "value") 
@Fetch (FetchMode.SELECT) 
private Map<String, String> metadataItems = new HashMap<String, String>(); 
5

我面临这个问题,我解决它使用

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

这将清除哪些由联接到做出导致重复子表。

0

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

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