【发布时间】: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 中的当前状态。