【问题标题】:Duplicates using left join fetch使用左连接提取重复
【发布时间】:2012-04-01 00:05:13
【问题描述】:

我正在使用 HQL 查询来获取某些记录。如果我使用LEFT JOIN FETCH,目标实体中的集合将包含重复记录。如果我只使用左连接,它不会。我猜当 Hibernate 懒加载记录时,它会避免重复。

"SELECT z FROM ", TableA.TABLE_NAME, " z ",  //
            "LEFT JOIN FETCH z.attributeX pv ", //
            "LEFT JOIN FETCH pv.attributeY anteil ", //
            "LEFT JOIN FETCH z.attributeB kma ", //
            "LEFT JOIN FETCH kma.attributeC ", //
            "WHERE anteil.attributeD.attributeE.id = :eId ", //
            "AND pv.attributeG.id = :gId ");

我的实体TableA 有一个指向TablePV (LEFT JOIN FETCH z.attributeX pv) 的链接

TablePV 拥有TableY (LEFT JOIN FETCH pv.attributeY anteil) 的集合

现在 Hibernate 将正确映射除 TablePV 的孩子之外的所有内容。它将包含多次相同的记录。 TableA 上的不同没有帮助,因为那里没有重复项。我可以手动删除那些我猜会非常糟糕的记录。

【问题讨论】:

  • 使用集合作为您的收藏。
  • AFAIK LEFT JOIN FETCH 和对它的限制是互斥的,因为限制可能会过滤掉集合中的元素以获取急切。也许你这里有不同的根本原因
  • 感谢您的回答。一个 Set 将解决问题,但它是一种变通方法,并且会产生其他问题(例如排序)。我不知道这是否是互斥的,因为这种情况是有道理的。我希望在同一个查询中获取记录,而不是使用另一个 select 语句。如果我只使用没有 FETCH 的 LEFT JOIN,就会发生这种情况。
  • Set 如何删除集合属性中的重复对象?!
  • 了解 LEFT JOIN 返回的内容:INNER JOIN 行加上由 NULL 扩展的不匹配的左表行。作为 LEFT JOIN 的一部分,始终知道您想要什么 INNER JOIN。在 LEFT JOIN ON 删除任何由 NULL 扩展的行之后,需要右表列不为 NULL 的 WHERE 或 INNER ON,即只留下 INNER JOIN 行,即“将 LEFT JOIN 转换为 INNER JOIN”。你有那个。

标签: hibernate jpa hql left-join fetch


【解决方案1】:

真正保证的唯一方法是在这些集合上使用SetSortedSet,而不是使用List。官方没有其他方法可以使用 Hibernate 来避免这个问题:

@OneToMany
private Set<AttributeY> attributeY;

您可以在旧的 Hibernate documentation 中阅读此提示:

使用急切获取集合的查询通常会返回 根对象的副本,但带有它们的集合 初始化。您可以通过 Set 过滤这些重复项。

或在newer one 上针对相同问题提供某种参考:

唯一的区别是 Set 不允许重复,但是这个 约束由 Java 对象契约而不是 数据库映射。

设置和订购

如果您想使用Set 并控制实体的顺序,you can use SortedSet 并在子实体上实现Comparable

@OneToMany
@SortNatural
private SortedSet<AttributeY> attributeY = new TreeSet<>();

还有:

@Entity
public class AttributeY implements Comparable<AttributeY> {

    @Override
    public int compareTo(AttributeY o) {
        return number.compareTo( o.getNumber() );
    }

}

要自定义排序逻辑,可以使用@SortComparator

注意事项

如果没有更多细节,很难说为什么在某些情况下使用 List 会发生这种情况,而在其他情况下则不会。但是你可以尝试使用实体的“业务密钥”来实现equals/hashCode方法:

使用集合时,提供正确的 equals/hashCode 非常重要 子实体的实现。在没有习俗的情况下 equals/hashCode 实现逻辑,Hibernate 会使用默认的 Java 基于引用的对象相等性可能会导致意外 混合分离和托管对象实例时的结果。

此外,您正在使用FETCH 别名pvanteil 应用条件。 Don't do that。并摆脱 JPQL (anteil.attributeD.attributeE.id) 上的“火车残骸”,因为这会使 Hibernate 创建奇怪的 SQL(例如多次执行相同的 JOIN 或无效的 SQL)。所以,让 JOIN 明确,不要在 WHERE 上使用 FETCH 别名:

LEFT JOIN FETCH z.attributeX
LEFT JOIN FETCH pv.attributeY
LEFT JOIN FETCH z.attributeB kma
LEFT JOIN FETCH kma.attributeC
LEFT JOIN pv.attributeY anteil
LEFT JOIN anteil.attributeD attributeD
LEFT JOIN attributeD.attributeE attributeE
LEFT JOIN z.attributeX pv
LEFT JOIN pv.attributeG attributeG
WHERE attributeE.id = :eId 
AND attributeG.id = :gId

如果重复是在根实体 TableA 中,DISTINCT 会有所帮助,但这不是你的情况。

【讨论】:

    【解决方案2】:

    尝试在查询中使用DISTINCT,例如SELECT DISTINCT z FROM Entity z...

    【讨论】:

    • 正如我的问题中提到的,这并不能解决问题。问题是,该集合包含重复项,而不是主要实体本身。
    猜你喜欢
    • 1970-01-01
    • 2015-08-02
    • 2018-08-24
    • 2018-03-06
    • 2011-12-22
    • 1970-01-01
    • 2014-02-04
    • 2013-11-23
    相关资源
    最近更新 更多