her*_*rti 50 java hibernate hql distinct one-to-many
我有一个一对多关系的2个类和一个有点奇怪的HQL查询.即使我已经阅读了一些已经发布的问题,但我似乎并不清楚.
Class Department{
@OneToMany(fetch=FetchType.EAGER, mappedBy="department")
Set<Employee> employees;
}
Class Employee{
@ManyToOne
@JoinColumn(name="id_department")
Department department;
}
Run Code Online (Sandbox Code Playgroud)
当我使用以下查询时,我得到重复的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有所不同.
ger*_*tan 90
这个问题在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();
Run Code Online (Sandbox Code Playgroud)
List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();
Run Code Online (Sandbox Code Playgroud)
所有这些示例都生成相同的SQL语句:
SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID
Run Code Online (Sandbox Code Playgroud)
想知道重复的原因在哪里?查看SQL结果集,Hibernate不会在外部联接结果的左侧隐藏这些重复项,但会返回驱动表的所有重复项.如果数据库中有5个订单,并且每个订单有3个订单项,则结果集将为15行.这些查询的Java结果列表将包含15个元素,所有元素都是Order.Hibernate只会创建5个Order实例,但SQL结果集的重复项将保留为对这5个实例的重复引用.如果您不理解最后一句,您需要阅读Java以及Java堆上的实例与对此类实例的引用之间的区别.(为什么左外连接?如果你有一个没有订单项的额外订单,结果集将是16行,其中NULL填充右侧,其中行项数据用于其他顺序.你想要订单,即使他们没有订单项,对吧?如果没有,请在HQL中使用内部联接提取.
默认情况下,Hibernate不会过滤掉这些重复的引用.有些人(不是你)真的想要这个.你怎么能过滤掉它们?像这样:Run Code Online (Sandbox Code Playgroud)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();
Run Code Online (Sandbox Code Playgroud)
最后一个是特别的.看起来您在这里使用SQL DISTINCT关键字.当然,这不是SQL,这是HQL.在这种情况下,这个不同只是结果转换器的快捷方式.是的,在其他情况下,HQL distinct将直接转换为SQL DISTINCT.不是在这种情况下:您无法在SQL级别过滤掉重复项,产品/连接的本质禁止这样做 - 您需要重复项或者您没有获得所需的所有数据.当结果集被编组到对象中时,所有这些重复的过滤都发生在内存中.同样显而易见的是,为什么基于行的"限制"操作(例如setFirstResult(5)和setMaxResults(10))不适用于这些类型的eager fetch查询.如果将结果集限制为特定行数,你随机切断数据.有一天,Hibernate可能足够聪明,知道如果你调用setFirstResult()或setMaxResults(),它不应该使用连接,而应该使用第二个SQL SELECT.试试吧,你的Hibernate版本可能已经足够智能了.如果没有,写两个查询,一个用于限制东西,另一个用于急切提取.你想知道为什么带有Criteria查询的例子没有忽略映射中的fetch ="join"设置,但是HQL并不关心?阅读下一个FAQ项目.你想知道为什么带有Criteria查询的例子没有忽略映射中的fetch ="join"设置,但是HQL并不关心?阅读下一个FAQ项目.你想知道为什么带有Criteria查询的例子没有忽略映射中的fetch ="join"设置,但是HQL并不关心?阅读下一个FAQ项目.
| 归档时间: |
|
| 查看次数: |
39912 次 |
| 最近记录: |