代码之家  ›  专栏  ›  技术社区  ›  raoulsson

hibernate条件使用fetchtype.earger多次返回子级

  •  107
  • raoulsson  · 技术社区  · 15 年前

    我有一个 Order 类的列表 OrderTransactions 我把它映射成一对多的休眠映射,就像这样:

    @OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
    public List<OrderTransaction> getOrderTransactions() {
        return orderTransactions;
    }
    

    这些 秩序 S也有一个字段 orderStatus ,用于按以下条件筛选:

    public List<Order> getOrderForProduct(OrderFilter orderFilter) {
        Criteria criteria = getHibernateSession()
                .createCriteria(Order.class)
                .add(Restrictions.in("orderStatus", orderFilter.getStatusesToShow()));
        return criteria.list();
    }
    

    这是可行的,结果如预期。

    现在 这是我的问题 :为什么,当我显式地将fetch类型设置为 EAGER 秩序 s在结果列表中出现多次?

    @OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    public List<OrderTransaction> getOrderTransactions() {
        return orderTransactions;
    }
    

    如何更改条件代码以获得与新设置相同的结果?

    7 回复  |  直到 6 年前
        1
  •  109
  •   Eran Medan    13 年前

    如果我正确理解了您的配置,这实际上是预期的行为。

    你也一样 Order 实例中的任何结果,但从现在起,您将使用 OrderTransaction ,它必须返回与常规SQL联接将返回的结果量相同的结果。

    所以事实上 应该 阿佩尔多次。这是作者(加文·金)自己很好地解释的。 here : 这两者都解释了为什么,以及如何仍然得到明显的结果。


    在冬眠中也提到过 FAQ :

    对于为集合启用外部联接提取的查询,Hibernate不会返回不同的结果。 (即使我使用 关键字?首先,您需要了解SQL以及外部联接如何工作。 在SQL中。如果你不完全理解和理解外部连接 SQL,不要继续阅读此常见问题解答项,请参阅SQL手册或 辅导的。否则您将无法理解以下解释 你会在冬眠论坛上抱怨这种行为。

    可能返回相同的重复引用的典型示例 订单对象:

    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行,右侧填充空值 侧,其中行项目数据用于其他订单。你需要命令 即使他们没有行项目,对吗?如果不是,请使用内部联接 把你的HQL拿来)。

    默认情况下,Hibernate不会筛选出这些重复引用。 有些人(不是你)真的想要这个。你怎么能过滤掉它们?

    这样地:

    Collection result = new LinkedHashSet( session.create*(...).list() );
    
        2
  •  89
  •   Eran Medan    13 年前

    除了ERAN提到的,另一种获得所需行为的方法是设置结果转换器:

    criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
    
        3
  •  40
  •   mathi    11 年前

    尝试

    @Fetch (FetchMode.SELECT) 
    

    例如

    @OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Fetch (FetchMode.SELECT)
    public List<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
    

    }

        4
  •  17
  •   agelbess    10 年前

    不要使用list和arraylist,而要使用set和hashset。

    @OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
    public Set<OrderTransaction> getOrderTransactions() {
        return orderTransactions;
    }
    
        5
  •  3
  •   easyScript    10 年前

    使用Java 8和流i在我的实用工具方法中添加这个返回状态:

    return results.stream().distinct().collect(Collectors.toList());
    

    流删除副本的速度非常快。我在我的实体类中使用注释,如下所示:

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "STUDENT_COURSES")
    private List<Course> courses;
    

    我认为贝瑟在我的应用程序中使用了方法会话,我需要数据库中的数据。当我做完的时候就闭嘴了。当然,将我的实体类设置为使用LeasyFetch类型。我去重构。

        6
  •  1
  •   Grigory Kislin    7 年前

    我有相同的问题来获取2个关联的集合:用户有2个角色(集合)和2个餐点(列表),并且餐点是重复的。

    @Table(name = "users")
    public class User extends AbstractNamedEntity {
    
       @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
       @Column(name = "role")
       @ElementCollection(fetch = FetchType.EAGER)
       @BatchSize(size = 200)
       private Set<Role> roles;
    
       @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
       @OrderBy("dateTime DESC")
       protected List<Meal> meals;
       ...
    }
    

    distinct不起作用(data-jpa查询):

    @EntityGraph(attributePaths={"meals", "roles"})
    @QueryHints({@QueryHint(name= org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false")}) // remove unnecessary distinct from select
    @Query("SELECT DISTINCT u FROM User u WHERE u.id=?1")
    User getWithMeals(int id);
    

    最后,我找到了两个解决方案:

    1. Change List to LinkedHashSet
    2. 使用只有字段“meat”和类型“load”的Entitygraph,它们按照声明加载角色(通过beging和batchsize=200来防止n+1问题):

    最终解决方案:

    @EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD)
    @Query("SELECT u FROM User u WHERE u.id=?1")
    User getWithMeals(int id);
    

    更新:除非JavaDoc org.springframework.data.jpa.repository.EntityGraph.EntityGraphType#FETCH

    由实体图的属性节点指定的属性将被视为fetchtype.earger,未指定的属性将被视为fetchtype.lazy。

    使用此类型还获取角色。

        7
  •  0
  •   ascripter    6 年前

    应用外部联接并带来重复的结果,这听起来不是一个很好的行为。唯一剩下的解决方案是使用流过滤我们的结果。感谢Java8提供了更简单的过滤方式。

    return results.stream().distinct().collect(Collectors.toList());