我有3个实体。
RequestHeader是一对多RequestDetails(RD多对一RH)
RequestDetail是一对多的RequestDetailResponse(RDH多对一RD)
以下是关系定义:
RequestHeader - > RequestDetail(注意该字段被更多地声明为Collection)
@OneToMany(mappedBy = "requestHeader", fetch = FetchType.EAGER)
Collection<RequestDetail> requestDetails = new HashSet<>();
RequestDetail - &gt; RequestHeader
@ManyToOne
@JoinColumn(name="requestHeaderID")
private RequestHeader requestHeader;
RequestDetail - &gt; DetailResponse
@OneToMany(mappedBy = "requestDetail", fetch = FetchType.EAGER)
Set<DetailResponse> detailResponses = new HashSet<>();
DetailResponse - &gt; RequestDetail
@ManyToOne
@JoinColumn(name="requestDetailID")
private RequestDetail requestDetail;
我的问题:当我从RequestHeader实体(通过公共获取者)获得RequestDetails的集合时,我获得了重复的#39; RequestDetails。玩sql,似乎是&#39;额外的&#39;细节反映了与DetailResponses的加入。这意味着例如
SELECT * FROM RequestHeader rh INNER JOIN RequestDetail rd on rd.RequestHeaderID = rh.RequestHeaderID
例如,返回5条记录,其中为
SELECT * FROM RequestHeader rh INNER JOIN RequestDetail rd on rd.RequestHeaderID = rh.RequestHeaderID INNER JOIN DetailResponse drs on drs.RequestDetailID = rd.RequestDetailID
返回10个结果,因为有多对一的DetailResponse扩展
当我从RequestHeader对象获取RequestDetails的集合时,我得到一个10号集合。现在,如果我将RequestHeader实体上的RequestDetails字段声明为Set而不是Collection,我会避免这个问题,但我认为既然我在RequestDetail实体上定义了主键,那么JPA管理应该是聪明到足以知道在这种情况下不会有重复。我错了什么?使用Set声明是正确的方法吗?
顺便说一下,这是我的主要声明,以防它们很重要。
RequestHeader:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer requestHeaderID;
RequestDetail:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer requestDetailID;
DetailResponse:
@Id
@TableGenerator(name="DetailResponseStore", table="PRIMARY_KEYS",
pkColumnName = "KEY_NAME", pkColumnValue = "DETAIL_RESPONSE", valueColumnName = "NEXT_VALUE", initialValue = 1, allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "DetailResponseStore")
private Integer detailResponseID;
答案 0 :(得分:0)
(回答我自己的问题)
简短而甜蜜的答案是,是的,Hibernate希望开发人员确保集合不包含重复项。如问题所示,第一个建议的方法是将集合存储在SET中。
(这是Hibernate文档中的常见问题解答主题(https://developer.jboss.org/wiki/HibernateFAQ-AdvancedProblems#jive_content_id_Hibernate_does_not_return_distinct_results_for_a_query_with_outer_join_fetching_enabled_for_a_collection_even_if_I_use_the_distinct_keyword):
)对于具有外部联接的查询,Hibernate不会返回不同的结果 为集合启用了获取(即使我使用了distinct 关键字)?
首先,您需要了解SQL以及OUTER JOIN在SQL中的工作方式。如果 你不完全理解和理解SQL中的外连接,不要 继续阅读此FAQ项目,但请参阅SQL手册或教程。 否则你不会理解以下解释和你 会在Hibernate论坛上抱怨这种行为。
可能返回相同引用的重复引用的典型示例 订单对象:
List result = session.createCriteria(Order.class) .setFetchMode("lineItems", FetchMode.JOIN) .list(); <class name="Order"> ... <set name="lineItems" fetch="join"> List result = session.createCriteria(Order.class) .list(); List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();
所有这些示例都生成相同的SQL语句:
SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID
想知道重复的原因在哪里?查看SQL结果集, Hibernate不会在外部左侧隐藏这些重复项 加入结果但返回驱动表的所有重复项。如果 您在数据库中有5个订单,每个订单有3个订单项, 结果集将是15行。这些查询的Java结果列表 将有15个元素,所有类型顺序。只有5个订单实例 由Hibernate创建,但SQL结果集的副本是 保留为对这5个实例的重复引用。如果你不 理解这最后一句话,你需要阅读Java和 Java堆上的实例与对它的引用之间的区别 这样的例子。
(为什么左外连接?如果你有一个没有线的额外订单 项目,结果集将是16行,其中NULL填充右侧 其中,订单项数据用于其他订单。你想要订单 即使他们没有订单项,对吧?如果没有,请使用内部联接 获取你的HQL)。
默认情况下,Hibernate不会过滤掉这些重复的引用。 有些人(不是你)真的想要这个。你怎么能过滤掉它们?
像这样:
Collection result = new LinkedHashSet( session.create*(...).list() );
LinkedHashSet过滤掉重复的引用(它是一个集合)和它 保留插入顺序(结果中元素的顺序)。那是 太容易了,所以你可以用许多不同的,更困难的方式来做到这一点:
注意:FAQ中的问题不是我的确切问题:我不是通过会话访问集合,而是允许Hibernate将子实体的集合填充为父节点上的字段。由于加入他们的子项,子集合会扩展,以将孙子集合填充为子项(其父项)上的字段。相似之处在于查看由hibernate生成的SQL我可以看到大左连接,我从常见问题解答中收集Hibernate不会在智能中烘焙(例如查看实体上的ID主键注释)来过滤他们说,重复(有些人想要那样)。他们也明确地说使用SET来过滤代码级别的重复项。