我遇到了HQL查询问题
三个班级
ClassOne是我的BusinessObject
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。
例如:
我想获得ClassOne的所有DTO(ClassThree),其状态现在为1,但在它们的历史早期它已经是2并且我想拥有最新的ClassTwo对象,其中有2个作为newStatus。
SELECT new ClassThree(c1.id, c1.status, c2)
FROM ClassOne c1
LEFT JOIN c1.classtwos c2 (...)
和(...)是我不知道该怎么做的地方,我甚至不确定它是否是一个加入/加入提取
环顾四周并尝试了很多,但没有任何线索。特别是对于连接提取,我得到了一些Hibernate错误,如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数据库所需的所有信息,但我真的希望有一个HQL继续使用,特别是出于维护原因等等...
使用变通方法解决方案进行更新:
获取状态为1的所有ClassOnes的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());
已经完成了。
答案 0 :(得分:2)
要包含没有历史记录的文档,我们应该使用LEFT JOIN
并测试空集合,然后我们使用子查询(SELECT COUNT(...)
)来检测从未处于状态2的所有文档。最后{{ 1}}用于获取具有指定状态的最后一个历史记录。
以下是HQL查询:
OR-clause
然后在您的查询中调用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)
id
属性的值作为对象放入数据库的顺序的指示符。