为父实体提取的重复关联实体

时间:2016-04-15 17:13:26

标签: hibernate jpa relationship

我有3个实体。

  1. RequestHeader
  2. RequestDetail
  3. RequestDetailResponse
  4. 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;
    

1 个答案:

答案 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来过滤代码级别的重复项。