2011-01-29 15 views
12

Sto provando a mettere insieme una query complicata usando Hibernate. Mi sono appoggiato alla Criteria, ma sto iniziando a sospettare che non sia possibile, e quindi qualsiasi suggerimento sarebbe utile.Criteri di ibernazione sui valori di raccolta

devo una struttura entità come la seguente:

public class Attribute { 
    private Integer id; 
    private String name; 
    private Set<Value> values; 
} 

public class Instance { 
    private Integer id; 
    private int instanceRef; 
    private Set<Value> values; 
} 

public class Value { 
    private Integer id; 
    private Attribute attribute; 
    private String localAttributeName; 
    private Instance instance; 
    private String value; 
} 

Queste entità sono correlate come ci si aspetterebbe:

value.attribute_id --> attribute.id 
value.instance_id --> instance.id 

Ora, mi piacerebbe essere in grado di prendere una serie di coppie attributo/valore (stringhe) e trova tutte le istanze che contengono tutte di esse. In Value, solo uno degli attributi e localAttributeName non sono nulli, quindi il nome dell'attributo potrebbe corrispondere a localAttributeName o attribute.name. E per complicare le cose un'ultima volta, l'indice univoco su Valore è attivo (istanza, attributo, valore) o (istanza, localAttributeName, valore) - cioè, all'interno di un'Istanza, qualsiasi Attributo dato può avere più Valori.

Questo è quello che ho finora:

public List<Instance> getMatchingInstances(Map<String, String> attrValues) { 
    Criteria crit = session.createCriteria(Instance.class, "i"); 
    for(Map.Entry<String, String> entry : attrValues) { 
     DetachedCriteria valueCrit = DetachedCriteria.forClass(Value.class, "v"); 

     // Do something here with valueCrit 

     crit.add(Subqueries.exists(valueCrit)); 
    } 
    return crit.list(); 
} 

Sulla base della ricerca che ho fatto, che cosa ho provato per quella sezione Fare qualcosa è:

// This would only check localAttributeName and not attribute.name. 
    // That's okay -- once I get the rest to work, I can figure this out. 
    valueCrit.add(Restrictions.eq("localAttributeName", entry.getKey()); 
    valueCrit.add(Restrictions.eq("value", entry.getValue()); 
    valueCrit.add(Restrictions.eqProperty("v.instance_id", "i.id")); 

Ma questo getta l'eccezione qui sotto, che sospetto mi sta dicendo che non posso farlo con Criteria, ma mi piacerebbe imparare diversamente:

java.lang.NullPointerException 
    at org.hibernate.loader.criteria.CriteriaQueryTranslator.getProjectedTypes(CriteriaQueryTranslator.java:341) 

Quale sarebbe il modo migliore per farlo?

risposta

17

Ho trovato la soluzione dopo alcune ore di battute. Spero che questo sia utile per gli altri. Ci sono stati tre punti principali che mi servivano per risolvere per rendere questo possibile:

  1. Aggiungi una proiezione
  2. Creare il corretto unisce
  3. correttamente map la subquery di nuovo ai criteri principali

I ho evidenziato ognuno di questi nel codice seguente.

Innanzitutto, per eliminare l'eccezione, ho scoperto che la subquery aveva bisogno di una proiezione, evidenziata di seguito. Ho appena fatto una proiezione sulla proprietà "id" di Instance.

In secondo luogo, per ottenere il join, ho utilizzato i metodi Criteria.createCriteria() per creare un join esterno sinistro. Poiché ho avuto più condizioni a diversi livelli del join, ho dovuto salvare i criteri uniti e allegare espressioni a loro separatamente. Questo mi permette di fare la mia espressione OR nella sottoquery.

Infine, ho dovuto aggiungere una clausola eqProperty() per mappare il subquery ai Criteri principali. Proprio come avrebbe dovuto essere nell'SQL risultante, ho usato: instance.id = i.id. Poiché avevo già mappato i criteri di istanza su "i" e aggiungevo questa clausola al criterio del valore, questo veniva tradotto in SQL: v.instance_id = i.id.

Ecco il codice di lavoro:

public List<Instance> getMatchingInstances(Map<String, String> attrValues) { 
    Criteria crit = session.createCriteria(Instance.class, "i"); 
    for(Map.Entry<String, String> entry : attrValues) { 
     String attrName = entry.getKey(); 
     String val = entry.getValue(); 

     // Create the subquery 
     DetachedCriteria valueCrit = DetachedCriteria.forClass(Value.class, "v"); 

     // Join the Attribute object (left outer join) 
     DetachedCriteria attrCrit = 
      valueCrit.createCriteria("attribute", CriteriaSpecification.LEFT_JOIN); 

     // Put together the OR statement on the Attribute joined criterion. 
     Criterion localAttr = Restrictions.eq("v.localAttributeName", attrName); 
     Criterion globalAttr = Restrictions.eq("name", attrName); 
     attrCrit.add(Restrictions.or(localAttr, globalAttr)); 

     // Simple column equality on the subquery criterion. 
     valueCrit.add(Restrictions.eq("value", val)); 

     // Map the subquery back to the outer query. 
     valueCrit.add(Restrictions.eqProperty("instance.id", "i.id")); 

     // Add the missing projection. 
     valueCrit.setProjection(Projections.property("id")); 

     // Add this subquery to the outer query. 
     crit.add(Subqueries.exists(valueCrit)); 
    } 
    return crit.list(); 
} 
+1

Funziona !!! .. Grazie. – Gaurav

+0

Ehi, @Jon, potresti aiutarmi con questo problema? Ecco il link http://stackoverflow.com/questions/22919886/hibernate-criteria-filtering-by-attributes-of-collection! Grazie! Riguarda qualcosa di molto simile! – Victor

+0

Quale versione di Hibernate è adatta a questa risposta? –

Problemi correlati