簡體   English   中英

關於集合值的Hibernate標准

[英]Hibernate criteria on collection values

我正在嘗試使用Hibernate組合一個復雜的查詢。 我一直傾向於Criteria,但我開始懷疑它不可能,所以任何建議都會有所幫助。

我有一個如下的實體結構:

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;
}

這些實體與您期望的相關:

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

現在,我希望能夠獲取一組屬性/值對(字符串)並查找包含所有這些屬性/值對的所有實例。 在Value中,attribute和localAttributeName中只有一個是非null,因此屬性名稱可以匹配localAttributeName或attribute.name。 最后一次使事情復雜化,Value上的唯一索引是(實例,屬性,值)或(實例,localAttributeName,value) - 也就是說,在實例中,任何給定的Attribute都可能有多個值。

這是我到目前為止:

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();
}

基於我已經完成的研究,我嘗試過的是做某事的部分是:

    // 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"));

但是這引發了下面的例外情況,我懷疑它是告訴我我不能用Criteria做到這一點,但是我很樂意學習:

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

這樣做最好的方法是什么?

經過幾個小時的撞擊,我找到了解決方案。 希望這對其他人有用。 為了使這個可行,我需要解決三個要點:

  1. 添加投影
  2. 創建正確的連接
  3. 正確地將子查詢映射回主要標准

我在下面的代碼中突出顯示了這些內容。

首先,為了擺脫異常,我發現子查詢需要一個投影,下面突出顯示。 我剛剛對Instance的“id”屬性進行了投影。

其次,為了獲得連接,我使用了Criteria.createCriteria()方法來創建左外連接。 因為我在連接的不同級別有多個條件,所以我必須保存連接的Criteria並將表達式分別附加到它們。 這讓我在子查詢中做我的OR表達式。

最后,我必須添加一個eqProperty()子句來將子查詢映射回主Criteria。 就像它需要在結果SQL中一樣,我使用了:instance.id = i.id. 因為我已經將實例標准映射到“i”並且正在將此子句添加到Value Criteria,所以這轉換為SQL:v.instance_id = i.id.

這是工作代碼:

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();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM