一对多关系无需使用“不同”即可获取重复的对象为什么?

如何解决一对多关系无需使用“不同”即可获取重复的对象为什么?

这个问题在Hibernate FAQ中有详尽的解释:

首先,您需要了解SQL以及OUTER JOIN在SQL中的工作方式。如果您不完全理解和理解SQL中的外部联接,请不要继续阅读此FAQ项,而请查阅SQL手册或教程。否则,您将无法理解以下说明,并且会在Hibernate论坛上抱怨此行为。可能返回相同Order对象的重复引用的典型示例:

List result = session.createCriteria(Order.class)  
                        .setFetchMode("lineItems", FetchMode.JOIN)  
                        .list();

<class name="Order">           
    <set name="lineItems" fetch="join">
    ...
</class>

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个元素,所有元素均为Order类型。Hibernate将仅创建5个Order实例,但是SQL结果集的重复项将保留为这5个实例的重复引用。如果您不明白这最后一句话,则需要阅读Java以及Java堆上的实例与对该实例的引用之间的区别。(为什么选择左外部联接?如果您还有其他订单,但没有任何订单项,结果集将为16行,右侧填充NULL,其中订单项数据用于其他订单。即使他们没有订单项,您也要订单,对吗?如果没有,请在您的HQL中使用内部联接提取。 默认情况下,Hibernate不会过滤掉这些重复的引用。有些人(不是您)实际上想要这个。如何过滤掉它们?像这样:

Collection result = new LinkedHashSet( session.create*(...).list() );

LinkedHashSet过滤掉重复的引用(它是一个集合),并保留插入顺序(结果中元素的顺序)。那太简单了,因此您可以通过许多不同且更困难的方式来做到这一点:

List result = session.createCriteria(Order.class)  
                        .setFetchMode("lineItems", FetchMode.JOIN)  
                        .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)  
                        .list();


<class name="Order">  
    ...  
    <set name="lineItems" fetch="join">

List result = session.createCriteria(Order.class)  
                        .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)  
                        .list();

List result = session.createQuery("select o from Order o left join fetch o.lineItems")  
                      .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) // Yes, really!  
                      .list();

List result = session.createQuery("select distinct o from Order o left join fetch o.lineItems").list();

最后一个很特别。看来您在这里使用SQL DISTINCT关键字。当然,这不是SQL,这是HQL。在这种情况下,这种区别只是结果转换器的捷径。是的,在其他情况下,HQL变量将直接转换为SQL DISTINCT。并非在这种情况下:您无法在SQL级别上过滤掉重复项,产品/联接的本质禁止这样做- 您想要重复项或没有获得所需的所有数据。当结果集被编组到对象中时,所有对重复项的过滤都在内存中进行。同样显而易见的是,为什么基于结果集基于行的“限制”操作(例如setFirstResult(5)和setMaxResults(10))无法与这类急切的获取查询一起使用。如果您将结果集限制为一定数量的行,您随机切断数据。有一天,Hibernate可能很聪明,知道如果调用setFirstResult()或setMaxResults(),则不应使用联接,而应使用第二个SQL SELECT。尝试一下,您的Hibernate版本可能已经足够聪明了。如果不是,则编写两个查询,一个查询用于限制内容,另一个查询用于快速获取。您是否想知道为什么带有Criteria查询的示例没有忽略映射中的fetch =“ join”设置,但是HQL不在乎?阅读下一个常见问题解答。您是否想知道为什么带有Criteria查询的示例没有忽略映射中的fetch =“ join”设置,但是HQL不在乎?阅读下一个常见问题解答。您是否想知道为什么带有Criteria查询的示例没有忽略映射中的fetch =“ join”设置,但是HQL不在乎?阅读下一个常见问题解答。

解决方法

我有一对多关系中的两个类和一个有点奇怪的HQL查询。即使我已经阅读了一些已经发布的问题,对我来说也不清楚。

Class Department{
   @OneToMany(fetch=FetchType.EAGER,mappedBy="department")
   Set<Employee> employees;
}
Class Employee{
   @ManyToOne
   @JoinColumn(name="id_department")
   Department department;
}

当我使用以下查询时,我得到重复的Department对象:

session.createQuery("select dep from Department as dep left join dep.employees");

因此,我必须使用不同的:

session.createQuery("select distinct dep from Department as dep left join dep.employees");

这是一种预期的行为吗?我认为这与SQL比较是不寻常的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?