Hibernate hql / sql join使用日期之间没有正确过滤

时间:2015-02-17 16:45:39

标签: sql hibernate hql

我希望有人能帮助我理解为什么会发生以下情况。我使用的是hibernate和HQL,但sqlserver或hsqldb的结果与hibernate生成的sql相同 - 我的日期不会过滤我的JOINed表。

问题的关键是一个具有多个连接的hql查询,其中我想根据日期过滤器过滤其中一个连接。结果不正确,包括我日期范围之外的数据。因此,例如,如果startMonthAndYear和endMonthAndYear是2015年1月20日和2015年12月,我在该范围之外看到结果(来自saAllocationList)(例如在2010年1月)。

HQL有限制,我无法在JOIN中执行子查询,这是我被告知可以工作的。我的问题似乎是我写的每一种方式都应该做同样的事情......但当然我不理解某些事情b / c它们都没有过滤我想要的日期。

我的初始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=?

但是所有内容都会返回相同的结果(包括我尝试过滤的范围之外的日期)。

感谢阅读,如果有更多我可以提供的信息,这将有助于让我知道这已经是摆弄什么都没有表现出来的日子我已经知道了#sb / c我&m;不太了解SQL。谢谢!

编辑:

我添加了一个sqlfiddle。 http://sqlfiddle.com/#!2/8348d/2不幸的是,sqlfiddle做了正确的事情(即过滤掉2010年的数据)。任何人都可以做错了然后向我解释为什么?

2 个答案:

答案 0 :(得分:0)

与C#和NHibernate有同样的问题。

数据库中的时间列是什么数据类型(DATEDATETIMETIMESTAMP)?查询中的数据类型必须与数据库中的数据类型相同。否则,WHERE子句将无法正确应用。

答案 1 :(得分:0)

我想通了,虽然它让我质疑我对hibernate中的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 in WITH应用的限定符。我的查询正在做什么(我认为)只是在合格的JOINed行的ID中加载,但是当我访问集合时它加载了整个集合(LAZILY)并且这让我一直关闭,直到我注意到延迟加载发生。

但是:正如博客提醒的那样 - 如果使用二级缓存,当你对JOIN进行限定时,它会将缓存置于风险b / c,你现在将这些值放入缓存中的方式与数据库真正不一致。为了调解这一点,您可以通过设置:

进一步告诉hibernate不要将内容放入缓存中
query.setHint("javax.persistence.cache.storeMode", "BYPASS");
query.setHint("javax.persistence.cache.retrieveMode", "BYPASS");

我没有使用二级缓存,我不认为我可以使用HQL进行上述操作,所以我不担心这个细节。

这让我对JOIN正在做什么感到好奇,因为我认为它避免了N + 1问题,但也许它只是加载一些id以避免必须做数据库工作来确定要加载的id,但是对象本身尚未加载。这是另一个问题/研究的话题。感谢那些花时间回答的人!