Hibernate hql/sql join using between dates not filtering properly
我希望有人能帮助我理解为什么会发生以下情况。我正在使用 hibernate 和 HQL,但在 sqlserver 或 hsqldb 中使用从 hibernate 生成的 sql 的结果是相同的 - 我的日期没有过滤我的 JOINed 表。
问题的症结在于具有多个连接的 hql 查询,其中我要根据日期过滤器过滤其中一个连接。结果错误地包含了我的日期范围之外的数据。因此,例如,如果 startMonthAndYear 和 endMonthAndYear 分别是 01/2015 和 12/2015,我看到的结果(来自 saAllocationList)超出了该范围(例如 01/2010)。
HQL 有一个限制,即我不能在 JOIN 本身中执行子查询,我被告知这是可行的。我的问题是,似乎我写的每一种方式都应该做同样的事情..但我当然不理解 b/c 他们都没有像我想要的那样过滤日期。
我最初的 HQL 尝试在 WHERE 子句中进行过滤。
1 2 3 4 5 6 7 8 | SELECT sa FROM StaffingAssignment sa INNER JOIN sa.workGroup wg LEFT JOIN sa.positionRole pr INNER JOIN sa.employee em INNER JOIN sa.sAAllocationList sal WHERE wg.programWorkGroupID = :id AND sa.sAAllocationPK.dateUnit BETWEEN :startMonthAndYear AND :endMonthAndYear |
上面生成如下sql:
1 2 3 4 5 6 | SELECT staffingas0_.ID AS ID1_6_, (trimmed BY me) FROM StaffingAllocation staffingas0_ INNER JOIN WorkGroup workgroup1_ ON staffingas0_.WorkGroupID=workgroup1_.ID LEFT OUTER JOIN PositionRole positionro2_ ON staffingas0_.PositionRoleID=positionro2_.ID INNER JOIN Employee employeeex3_ ON staffingas0_.EmployeeID=employeeex3_.ID INNER JOIN SAAllocation saallocati4_ ON staffingas0_.ID=saallocati4_.SAID WHERE workgroup1_.ProgramWorkGroupID=? AND (saallocati4_.DateUnit BETWEEN ? AND ?) |
我已经用几种不同的方式重写了 hsql:
在 where 子句中使用子查询来尝试获取要加载的 id 列表:
1 2 3 4 5 6 7 8 9 10 11 | SELECT sa FROM StaffingAssignment sa INNER JOIN sa.sAAllocationList INNER JOIN sa.workGroup wg LEFT JOIN sa.positionRole pr INNER JOIN sa.employee em WHERE wg.programWorkGroupID = :id AND sa.id IN (SELECT saa.staffingAllocation.id FROM SAAllocation saa WHERE saa.sAAllocationPK.dateUnit BETWEEN :startMonthAndYear AND :endMonthAndYear) |
生成以下sql:
1 2 3 4 5 6 | SELECT staffingas0_.ID AS ID1_6_, (trimmed BY me) FROM StaffingAllocation staffingas0_ INNER JOIN SAAllocation saallocati1_ ON staffingas0_.ID=saallocati1_.SAID INNER JOIN WorkGroup workgroup2_ ON staffingas0_.WorkGroupID=workgroup2_.ID LEFT OUTER JOIN PositionRole positionro3_ ON staffingas0_.PositionRoleID=positionro3_.ID INNER JOIN Employee employeeex4_ ON staffingas0_.EmployeeID=employeeex4_.ID WHERE workgroup2_.ProgramWorkGroupID=? AND (staffingas0_.ID IN (SELECT saallocati5_.SAID FROM SAAllocation saallocati5_ WHERE saallocati5_.DateUnit BETWEEN ? AND ?)) |
在 HQL 中使用
1 2 3 4 5 6 7 8 | SELECT sa FROM StaffingAssignment sa INNER JOIN sa.sAAllocationList sal WITH sal.sAAllocationPK.dateUnit BETWEEN :startMonthAndYear AND :endMonthAndYear INNER JOIN sa.workGroup wg LEFT JOIN sa.positionRole pr INNER JOIN sa.employee em WHERE wg.programWorkGroupID = :id |
生成以下sql:
1 2 3 4 5 6 | SELECT staffingas0_.ID AS ID1_6_, (trimmed BY me) FROM StaffingAllocation staffingas0_ INNER JOIN SAAllocation saallocati1_ ON staffingas0_.ID=saallocati1_.SAID AND (saallocati1_.DateUnit BETWEEN ? AND ?) INNER JOIN WorkGroup workgroup2_ ON staffingas0_.WorkGroupID=workgroup2_.ID LEFT OUTER JOIN PositionRole positionro3_ ON staffingas0_.PositionRoleID=positionro3_.ID INNER JOIN Employee employeeex4_ ON staffingas0_.EmployeeID=employeeex4_.ID WHERE workgroup2_.ProgramWorkGroupID=? |
但一切都返回相同的结果(包括我试图过滤的范围之外的日期)。
感谢您阅读,如果我可以提供更多信息,这将有助于让我知道这几天一直在摆弄什么都没有显示,而且我知道这是 b/c 我对 SQL 的理解不够好。谢谢!
编辑:
我添加了一个 sqlfiddle。 http://sqlfiddle.com/#!2/8348d/2 不幸的是,sqlfiddle 做了正确的事情(即过滤掉 2010 年的数据)。谁能让它做错事,然后向我解释原因?
我想通了,尽管它让我质疑我对hibernate中 JOIN 的理解。这个博客很好地解释了解决方案:
http://java-persistence-performance.blogspot.com/2012/04/objects-vs-data-and-filtering-join.html
我需要使用 JOIN FETCH:
1 2 3 4 5 6 7 8 | SELECT sa FROM StaffingAssignment sa INNER JOIN sa.workGroup wg LEFT JOIN sa.positionRole pr INNER JOIN sa.employee em INNER JOIN FETCH sa.sAAllocationList sal WHERE wg.programWorkGroupID = :id AND sa.sAAllocationPK.dateUnit BETWEEN :startMonthAndYear AND :endMonthAndYear |
通过使用 JOIN FETCH,它强制 hibernate 执行限定符作为 fetch 的一部分。这本质上是一个 EAGER 加载集合 JOINed in WITH the qualifier apply。我的查询所做的(我认为)只是加载了合格的 JOINed 行的 id,但是当我访问集合时,它加载了整个集合(LAZILY),这让我很失望,直到我注意到发生了延迟加载。
但是:正如博客所警告的那样 - 如果使用二级缓存,它会使缓存处于风险 b/c 当您限定 JOIN 时,您现在将值放入缓存中的方式确实与数据库。为了解决这个问题,您可以通过设置进一步告诉 hibernate 不要将内容放入缓存中:
1 2 | query.setHint("javax.persistence.cache.storeMode","BYPASS"); query.setHint("javax.persistence.cache.retrieveMode","BYPASS"); |
我没有使用二级缓存,我认为我不能用 HQL 完成上述操作,所以我不担心这个细节。
这让我很好奇 JOIN 在做什么,因为我认为它避免了 N 1 问题,但也许它只是加载一些 id 以避免必须进行数据库工作来确定要加载的 id ,但对象本身尚未加载。那是另一个问题/研究的主题。感谢那些花时间回答的人!
在使用 C# 和 NHibernate 时遇到了同样的问题。
您在数据库中的时间列是什么数据类型(