проблема с "выберите новый объект ... присоединиться ... где" - PullRequest
3 голосов
/ 09 июня 2010

У меня проблема с 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 с определенным статусом и, если он существует, новейшим ClassTwo с определенным newStatus.
Например:
Я хотел бы получить все DTO (ClassThree) ClassOne, чей статус теперь равен 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 в качестве моего поля.

Заранее спасибо,
Jacob

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 продолжал работать, особенно по причинам обслуживания и т. Д. ...

Обновление с обходным решением:

Получение идентификаторов всех ClassOnes со статусом 1

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

Теперь я получаю все DTO, которые были в статусе 2 (спасибо Ивану) с:

Именованный запрос 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());

После этого я удаляю все идентификаторы, найденные в моем списке. Идентификаторы

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 Ответ

2 голосов
/ 09 июня 2010

Чтобы включить документы без истории, мы должны использовать 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) для вашего запроса, чтобы получить правильный результат.

Вот и все!

Обратите внимание, я сделал предположение, что мы можем использовать значение id атрибут History в качестве индикатора порядка, в котором объекты были помещены в вашбазы данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...