Java Hibernate条件多次返回FetchType.EAGER的子项
我有一个Java Hibernate条件多次返回FetchType.EAGER的子项,java,hibernate,Java,Hibernate,我有一个Order类,它有一个OrderTransactions列表,我用一对多Hibernate映射将其映射,如下所示: @OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL) public List<OrderTransaction> getOrderTransactions() { return orderTransactions; } 这是可行的,结果正如预期的那样 现
Order
类,它有一个OrderTransactions
列表,我用一对多Hibernate映射将其映射,如下所示:
@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
这是可行的,结果正如预期的那样
现在这是我的问题:为什么当我将fetch类型显式设置为EAGER
时,顺序
会在结果列表中多次出现
@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
@OneToMany(targetEntity=OrderTransaction.class,fetch=FetchType.EAGER,cascade=CascadeType.ALL)
公共列表getOrderTransactions(){
退货订单交易;
}
如果我正确理解您的配置,如何更改标准代码以达到与新设置相同的结果?这实际上是预期的行为 您在任何结果中都会得到相同的
Order
实例,但由于现在您正在使用OrderTransaction
执行联接,因此它必须返回与常规sql联接相同数量的结果
所以实际上它应该出现多次。作者(加文·金)本人对此做了很好的解释:
它既解释了为什么,又解释了如何仍然获得不同的结果
在Hibernate中也提到: 对于为集合启用外部联接获取的查询,Hibernate不会返回不同的结果(即使我使用了不同的 关键词)?首先,您需要了解SQL以及外部联接是如何工作的 在SQL中。如果您不完全理解和理解 SQL,请不要继续阅读此常见问题解答项目,请查阅SQL手册或 辅导的否则,您将无法理解以下解释 你会在Hibernate论坛上抱怨这种行为 可能返回相同的重复引用的典型示例 订单对象:
List result = session.createCriteria(Order.class)
.setFetchMode("lineItems", FetchMode.JOIN)
.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() );
除了Eran提到的内容之外,获得所需行为的另一种方法是设置结果转换器:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
试一试
比如说
@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Fetch (FetchMode.SELECT)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;
@OneToMany(targetEntity=OrderTransaction.class,fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@Fetch(FetchMode.SELECT)
公共列表getOrderTransactions(){
退货订单交易;
}不要使用List和ArrayList,而要使用Set和HashSet
@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public Set<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
@OneToMany(targetEntity=OrderTransaction.class,cascade=CascadeType.ALL)
公共集getOrderTransactions(){
退货订单交易;
}
使用Java 8和Streams,我在实用程序方法中添加了以下返回语句:
return results.stream().distinct().collect(Collectors.toList());
流删除重复非常快。我在实体类中使用注释,如下所示:
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "STUDENT_COURSES")
private List<Course> courses;
@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@JoinTable(name=“学生课程”)
私人名单课程;
我认为在我的应用程序中使用session-in方法更好,因为我需要数据库中的数据。当我完成的时候。当然,将我的实体类设置为使用leasyfetch类型。我去重构。我在获取两个相关集合时遇到了同样的问题:用户有两个角色(集合)和两份膳食(列表),膳食是重复的
@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;
...
}
最后,我找到了两种解决方案:
@EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD)
@Query("SELECT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);
应用外部联接并带来重复结果听起来不是一个好行为。剩下的唯一解决方案是使用流过滤结果。感谢java8提供了更简单的过滤方式
return results.stream().distinct().collect(Collectors.toList());
而不是使用如下黑客:
而不是设置
列表
criteria.setResultTransformer(criteria.DISTINCT\u ROOT\u实体)代码>
它会修改生成的sql查询,因此在其中有一个
DISTINCT
您是否尝试过启用show_sql以查看下面的情况?请同时添加OrderTransaction和Order类代码。\n即使您理解以下解释,您也可能会在Hibernate论坛上抱怨这种行为,因为这是一种愚蠢的行为!没错,汤姆,我忘了加文·金的傲慢态度。他还说,“Hibernate默认情况下不会过滤掉这些重复引用。有些人(不是你)真的想要这个,当人们真的想要这个的时候,我会感兴趣的。@TomAnderson是的,没错。为什么会有人需要这些复制品?我问这个纯粹是出于好奇,因为我不知道。。。您可以自己创建副本,任意多个副本…-)唉。这实际上是Hibernate缺陷,IMHO。我想优化我的查询,所以
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "STUDENT_COURSES")
private List<Course> courses;
@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;
...
}
@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);
@EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD)
@Query("SELECT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);
return results.stream().distinct().collect(Collectors.toList());
q.select(emp).distinct(true);