【问题标题】:problem with "select new Object ... join ... where"“选择新对象......加入......在哪里”的问题
【发布时间】:2010-06-11 20:49:47
【问题描述】:

我遇到了 HQL 查询问题

三类

ClassOne 是我的业务对象

public class ClassOne {  
  private int id;  
  private int status;
  private Set<ClassTwo> classTwos;  
  + other fields/getters/setters/constructor etc  
}

ClassTwo 在一组 ClassOne 中被引用,是 ClassOne 对象的一种历史

public class ClassTwo {  
  private int id;  
  private int oldStatus;  
  private int newStatus;  
  private String message;  
  //+ getters/setters/constructor etc  
}

ClassThree 是我的 DTO/VO,只有一个 classTwo(不是整个历史)

public class ClassThree {  
  private int id;  
  private int status;  
  private ClassTwo classTwo;  
  public ClassThree(int pId, int pStatus, ClassTwo pClassTwo) {  
    id=pId;  
    status=pStatus;  
    classTwo=pClassTwo;   
  }
  //+ getters/setters etc
}

现在我想创建一个像这样的 HQL 查询:
我想获取具有特定状态的 ClassThree 的所有对象,如果它存在具有特定 newStatus 的最新 ClassTwo。
例如:
我想获取现在状态为 1 的 ClassOne 的所有 DTO(ClassThree),但在他们的历史早期它一直是 2,我想拥有最新的 ClassTwo 对象,它的 newStatus 为 2。

SELECT new ClassThree(c1.id, c1.status, c2)  
FROM ClassOne c1  
LEFT JOIN c1.classtwos c2 (...)

and (...) 是我不知道该怎么做的地方,我什至不确定它是否是 join / join fetch

环顾四周,已经尝试了很多,但没有任何线索。特别是在加入获取时,我会遇到一些休眠错误,例如org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list

这样获取 BusinessObject 没问题

SELECT distinct(c1)
FROM ClassOne c1  
LEFT OUTER JOIN FETCH c1.classtwos c2  

我将 ClassTwos 作为我的领域。

提前致谢,
雅各布

P.S.:有一点可能很重要,ClassTwo 没有引用 ClassOne!!

P.P.S:解决我的问题的简单 SQL 查询看起来或多或少是这样的:

select * from classone as c1 left join (select * from classtwo where newstatus = 2) c2 on c1.id=c2.id_classone whete c1.status = 1 

此查询有效并获取我的 PostGreSQL DB 上所需的所有信息,但我真的希望有一个 HQL 可以继续使用,尤其是出于维护等原因...

更新解决方法:

获取所有状态为 1 的 ClassOne 的 id

Collection<Integer> ids = null;
ids = (Collection<Integer>) getHibernateTemplate().execute(
  new HibernateCallback() {
    public Object doInHibernate(Session pSession) throws HibernateException, SQLException {
      return getDocumentIds(pSession, pStatus);
    }
  }
);

现在我得到了所有处于状态 2 的 DTO(感谢 Ivan):

命名查询Document.dto.with.transfer

SELECT new DocumentDTO(d.id, d.status, histo)
FROM Document d
LEFT JOIN d.histories histo
WHERE 
  d.id in (:ids)
AND
  (histo.id = 
    SELECT MAX(innerhisto.id) 
    FROM Document innerd 
    JOIN innerd.histories innerhisto
    WHERE d.id = innerd.id AND innerhisto.newStatus = 21)

(在我的代码中,我使用了一些命名查询)

List<DocumentDTO> lRes = new ArrayList<DocumentDTO>();
Query lQuery = getSession(false).getNamedQuery("Document.dto.with.transfer");
lQuery.setParameterList("ids", ids);
lResultList.addAll(lQuery.list());

然后我从我的列表 id 中删除所有已找到的 ID

for (DocumentDTO dto : lResultList) {
  ids.remove(dto .getId());
}

我使用 DTO 的第二个构造函数执行第三个查询,使用虚拟对象初始化我的历史记录。

命名查询Document.dto.simple

SELECT new DocumentDTO(d.id, d.status)
FROM Document d
WHERE 
  d.id in (:ids)

(另一个命名查询)

lQuery = getSession(false).getNamedQuery("Document.dto.simple");
lQuery.setParameterList("ids", ids);
lResultList.addAll(lQuery.list());

它已经完成了。

【问题讨论】:

  • 您同时使用 Java 和 NHibernate?我以为 NHibernate 是为 .NET 设计的?
  • 不,只是标记错了,实际上是Java + Hibernate
  • 我不明白从您的描述中检索唯一 c2 的标准。
  • 我只是不想得到一个特定的 c2,因为只有一个对我来说可能很有趣。现实世界的例子:ClassOne 是一个文档,状态是对文档执行的最后一个操作(读取、写入、移动等)。 ClassTwo 文档的历史记录(读取、写入访问、移动等)。我想在文档上显示消息和上次写入访问的先前状态(但仅当它存在时)以及我的文档以及它在 ClassThree 中的当前状态。

标签: java hibernate hql


【解决方案1】:

要包含没有历史记录的文档,我们应该使用 LEFT JOIN 并测试空集合,然后我们使用子查询 (SELECT COUNT(...)) 来检测所有从未处于状态 2 的文档。最后一个 OR-clause 用于获取具有指定状态的最后历史记录。

这是 HQL 查询:

SELECT new DocumentDto(doc.id, doc.status, hist) 
FROM Document doc 
LEFT JOIN doc.histories hist 
WHERE doc.status = :docStatus 
AND (size(doc.histories) = 0 
OR (SELECT COUNT(innerhist) 
    FROM Document innerdoc JOIN innerdoc.histories innerhist 
    WHERE innerdoc.id=doc.id AND innerhist.newStatus = :historyStatus) = 0
OR (hist.newStatus = :historyStatus AND hist.id = 
    (SELECT max(innerhist.id) 
     FROM Document innerdoc 
     JOIN innerdoc.histories innerhist 
     WHERE innerdoc.status = :docStatus AND innerhist.newStatus = :historyStatus))

然后在您的查询中调用setParameter("historyStatus", 2)setParameter("docStatus", 1) 以获得正确的结果。

就是这样!

请注意,我做了一个假设,我们可以使用Historyid 属性值作为对象放入数据库中的顺序的指示符。

【讨论】:

  • 您对 id 的假设是正确的。但查询仅在存在具有 newStatus = 2 的 ClassTwo 对象时提取 ClassThrees。我需要的东西很像我添加到问题中的 SQL 查询。
  • Ivan,更新后的 HQL 返回每个状态为 2 的历史条目,而不仅仅是最后一个。
  • @Tomislav 我知道。但是这个HQL对应上次更新时jacob提供的SQL。
  • @Ivan,你对 sql 的看法是对的,但举个例子来说明我想要的东西或多或少,就像 Tomislav 说的那样,我真的只想要最后一个。对于 status = 2 的每个历史条目,此查询返回 DTO
  • @Ivan,只剩下一个小问题(或者说相当大)。它只获取至少一次处于状态 2 的 DTO,但我正在寻找的是:我希望所有 DTO 都处于状态 1,如果它们从未处于状态 2 并且没有任何历史记录如果 DTO 之前处于状态 2,则状态为 2 的最后历史记录。到目前为止非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 2012-07-15
  • 2017-04-04
相关资源
最近更新 更多