Domanda

Sto cercando di mettere insieme una query complessa usando Hibernate. Ho sporgendosi verso criteri, ma comincio a sospettare che non è possibile, e così tutti i suggerimenti sarebbe utile.

Ho una struttura di 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 correlati 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 trovare tutte le istanze che contengono tutti di loro. In Valore, solo uno di attributo e localAttributeName sono non nullo, così il nome attributo può corrispondere sia localAttributeName o attribute.name. E per le cose complicate per l'ultima volta, l'indice univoco sul Valore è acceso (esempio, l'attributo, valore) o (esempio, localAttributeName, value) -. Che è, all'interno di un grado, un dato attributo 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 quel Do sezione 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 genera l'eccezione di sotto, che ho il sospetto mi sta dicendo che non posso farlo con criteri, ma mi piacerebbe imparare altrimenti:

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

Quale sarebbe il modo migliore per andare a fare questo?

È stato utile?

Soluzione

I figured out the solution after a few hours of banging on it. Hopefully, this is of use to others. There were three main points that I needed to solve to make this feasible:

  1. Add a Projection
  2. Create the proper joins
  3. Properly map the subquery back to the main criteria

I've highlighted each of these in the below code.

First, to get rid of the exception, I discovered that the subquery needed a projection, highlighted below. I just did a projection on the "id" property of Instance.

Second, to get the join, I used the Criteria.createCriteria() methods to create a left outer join. Because I had multiple conditions at different levels of the join, I had to save the joined Criteria and attach expressions to them separately. This let me do my OR expression in the subquery.

Finally, I had to add an eqProperty() clause to map the subquery back to the main Criteria. Just like it would need to be in the resulting SQL, I used: instance.id = i.id. Because I had already mapped the Instance Criteria to "i" and was adding this clause to the Value Criteria, this translated to the SQL: v.instance_id = i.id.

Here's the working code:

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();
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top