Hibernate hql/sql 在日期之间使用未正确过滤的加入
Hibernate hql/sql join using between dates not filtering properly
我希望有人能帮助我理解为什么会出现以下情况。我正在使用 hibernate 和 HQL,但是 sqlserver 或 hsqldb 的结果与从 hibernate 生成的 sql 相同 - 我的日期没有过滤我的 JOINed table.
问题的症结在于具有多个连接的 hql 查询,其中一个连接我想根据日期过滤器进行过滤。结果错误地包含了超出我的日期范围的数据。因此,例如,如果 startMonthAndYear 和 endMonthAndYear 是 01/2015 和 12/2015,我看到的结果(来自 saAllocationList)超出了该范围(例如在 01/2010)。
HQL 有一个限制,我不能在 JOIN 本身中执行子查询,这是我被告知可以工作的。我的问题是我写的每一种方式似乎都应该做同样的事情..但当然我不理解某些东西 b/c none 他们像我想要的那样过滤日期。
我最初的 HQL 尝试在 WHERE 子句中进行过滤。
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:
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 列表:
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:
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 中使用 WITH
关键字尝试在 JOIN 本身中进行过滤:
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:
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=?
但所有结果 returns 都是相同的(包括超出我试图过滤的范围的日期)。
感谢您的阅读,如果有任何更多信息可以包含,这将有助于让我知道这几天一直在摆弄,没有什么可展示的,我知道这是 b/c 我不理解 SQL 够好了。谢谢!
编辑:
我添加了一个 sqlfiddle。 http://sqlfiddle.com/#!2/8348d/2 不幸的是 sqlfiddle 做了正确的事情(即过滤掉 2010 年的数据)。谁能让它做错事然后向我解释为什么?
使用 C# 和 NHibernate 时遇到同样的问题。
您在数据库中的时间列是什么数据类型(DATE
、DATETIME
、TIMESTAMP
)?查询中的数据类型必须与数据库中的相同。否则 WHERE
子句将无法正确应用。
我想通了,尽管这让我质疑我对休眠中的 JOIN 的理解。这个博客很好地解释了解决方案:
http://java-persistence-performance.blogspot.com/2012/04/objects-vs-data-and-filtering-join.html
我需要使用 JOIN FETCH:
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 在提取过程中执行限定符。这本质上是一个 EAGER 加载集合 JOINed with the qualifier applied。我的查询所做的(我认为)只是加载到合格的 JOINed 行的 ID 中,但是当我访问集合时,它加载了整个集合(LAZILY),这让我很困惑,直到我注意到发生了延迟加载。
但是:正如博客所警告的那样 - 如果使用二级缓存,它会使缓存处于危险之中 b/c 当您限定 JOIN 时,您现在将值放入缓存的方式与数据库。为了解决这个问题,您可以通过设置进一步告诉 hibernate 不要将内容放入缓存中:
query.setHint("javax.persistence.cache.storeMode", "BYPASS");
query.setHint("javax.persistence.cache.retrieveMode", "BYPASS");
我没有使用二级缓存,我认为我不能用 HQL 完成上述操作,所以我不担心这个细节。
这让我对 JOIN 的作用感到好奇,因为我认为它避免了 N+1 问题,但也许它只是加载一些 ID 以避免必须执行数据库工作来确定要加载的 ID ,但对象本身尚未加载。不过,这是另一个 question/research 的主题。感谢那些花时间回答的人!
我希望有人能帮助我理解为什么会出现以下情况。我正在使用 hibernate 和 HQL,但是 sqlserver 或 hsqldb 的结果与从 hibernate 生成的 sql 相同 - 我的日期没有过滤我的 JOINed table.
问题的症结在于具有多个连接的 hql 查询,其中一个连接我想根据日期过滤器进行过滤。结果错误地包含了超出我的日期范围的数据。因此,例如,如果 startMonthAndYear 和 endMonthAndYear 是 01/2015 和 12/2015,我看到的结果(来自 saAllocationList)超出了该范围(例如在 01/2010)。
HQL 有一个限制,我不能在 JOIN 本身中执行子查询,这是我被告知可以工作的。我的问题是我写的每一种方式似乎都应该做同样的事情..但当然我不理解某些东西 b/c none 他们像我想要的那样过滤日期。
我最初的 HQL 尝试在 WHERE 子句中进行过滤。
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:
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 列表:
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:
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 中使用 WITH
关键字尝试在 JOIN 本身中进行过滤:
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:
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=?
但所有结果 returns 都是相同的(包括超出我试图过滤的范围的日期)。
感谢您的阅读,如果有任何更多信息可以包含,这将有助于让我知道这几天一直在摆弄,没有什么可展示的,我知道这是 b/c 我不理解 SQL 够好了。谢谢!
编辑:
我添加了一个 sqlfiddle。 http://sqlfiddle.com/#!2/8348d/2 不幸的是 sqlfiddle 做了正确的事情(即过滤掉 2010 年的数据)。谁能让它做错事然后向我解释为什么?
使用 C# 和 NHibernate 时遇到同样的问题。
您在数据库中的时间列是什么数据类型(DATE
、DATETIME
、TIMESTAMP
)?查询中的数据类型必须与数据库中的相同。否则 WHERE
子句将无法正确应用。
我想通了,尽管这让我质疑我对休眠中的 JOIN 的理解。这个博客很好地解释了解决方案: http://java-persistence-performance.blogspot.com/2012/04/objects-vs-data-and-filtering-join.html
我需要使用 JOIN FETCH:
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 在提取过程中执行限定符。这本质上是一个 EAGER 加载集合 JOINed with the qualifier applied。我的查询所做的(我认为)只是加载到合格的 JOINed 行的 ID 中,但是当我访问集合时,它加载了整个集合(LAZILY),这让我很困惑,直到我注意到发生了延迟加载。
但是:正如博客所警告的那样 - 如果使用二级缓存,它会使缓存处于危险之中 b/c 当您限定 JOIN 时,您现在将值放入缓存的方式与数据库。为了解决这个问题,您可以通过设置进一步告诉 hibernate 不要将内容放入缓存中:
query.setHint("javax.persistence.cache.storeMode", "BYPASS");
query.setHint("javax.persistence.cache.retrieveMode", "BYPASS");
我没有使用二级缓存,我认为我不能用 HQL 完成上述操作,所以我不担心这个细节。
这让我对 JOIN 的作用感到好奇,因为我认为它避免了 N+1 问题,但也许它只是加载一些 ID 以避免必须执行数据库工作来确定要加载的 ID ,但对象本身尚未加载。不过,这是另一个 question/research 的主题。感谢那些花时间回答的人!