“选择新对象...加入...在哪里”的问题

时间:2010-06-09 14:26:05

标签: java hibernate hql

我遇到了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());

已经完成了。

1 个答案:

答案 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属性的值作为对象放入数据库的顺序的指示符。