2010-06-09 5 views
3

Je vais avoir un problème avec une requête HQLavec "select new Object ... rejoindre ... où"

Trois classes

ClassOne est mon BusinessObject

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

ClassTwo est référencé dans un ensemble de ClassOne et est en quelque sorte de l'histoire d'un objet de ClassOne

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

ClassThre e est ma DTO/VO avec un seul classTwo (pas la histoire)

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 
} 

Maintenant, je voudrais créer une requête HQL comme ceci:
J'aimerais obtenir tous les objets de ClassThree avec certain statut et s'il existe le plus récent ClassTwo avec un certain newStatus.
Par exemple:
Je voudrais obtenir tous les DTOs (ClassThree) de ClassOne dont le statut est maintenant 1, mais plus tôt dans leur histoire il a été 2 et j'aimerais avoir le dernier objet ClassTwo qui a 2 comme newStatus.

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

et (...) est là, je ne sais pas quoi faire, je ne suis même pas sûr que ce soit une jointure/join fetch

regardé autour et déjà essayé beaucoup, mais aucune idée. Surtout avec la recherche de jointure je reçois quelques erreurs Hibernate comme org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list.

Récupérer l'BusinessObject comme ça est pas un problème

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

et je reçois les ClassTwos comme mon champ.

Merci à l'avance,
Jacob

P.S .: Une chose pourrait être important, ClassTwo n'a pas de référence à ClassOne !!

PPS: La simple requête SQL qui résout mon problème semble plus ou moins comme ça:

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

Cette requête fonctionne et obtient toutes les informations nécessaires sur mon PostGreSQL DB, mais je voudrais vraiment avoir un HQL de continuer à travailler avec, en particulier pour des raisons de maintenance et ainsi de suite ...

Mise à jour avec une solution de contournement:

Obtenir les ids de tous les ClassOnes avec un état 1

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

Maintenant je tous les DTO qui sont en état 2 (grâce à Ivan) avec:

requête nommée Document.dto.with.transfert

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) 

(dans mon code que j'utilise des requêtes nommées)

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

ensuite supprimer toutes les ID déjà trouvés de ma liste ids

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

Je fais une troisième requête utiliser un second constructeur pour le DTO, en initialisant mon histoire avec un objet factice.

requête nommée Document.dto.simple

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

(une autre requête nommée)

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

et il est fait.

+0

Vous utilisez Java et NHibernate ensemble? Je pensais que NHibernate était pour .NET? –

+0

Non, juste étiqueté faux, en fait son Java + Hibernate – jacob

+0

Je ne comprends pas les critères pour récupérer un c2 unique de votre description. –

Répondre

2

Pour inclure des documents sans histoire, nous devrions utiliser LEFT JOIN et tester pour la collecte vide, nous utilisons sous_requête (SELECT COUNT(...)) pour détecter tous les documents qui ont jamais été dans l'état 2. Le dernier OR-clause est pour aller chercher la dernière histoire avec la statut spécifié.

Voici la requête 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)) 

appellent ensuite setParameter("historyStatus", 2) et setParameter("docStatus", 1) sur votre requête pour obtenir le bon résultat.

C'est tout!

S'il vous plaît noter, j'ai fait une hypothèse, que nous pouvons utiliser une valeur de id attribut de History comme un indicateur de l'ordre dans lequel les objets ont été mis dans votre base de données.

+0

Votre hypothèse sur l'ID est correcte. mais la requête extrait uniquement les ClassThrees si un objet ClassTwo avec newStatus = 2 existe. La chose dont j'ai besoin est plutôt comme la requête SQL que j'ai ajoutée à la question. – jacob

+0

@jacob, j'ai mis à jour ma réponse – yatskevich

+0

Ivan, le HQL mis à jour retourne chaque entrée hist avec statut = 2 au lieu de seulement le dernier. –