[英]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)
這樣做最好的方法是什么?
經過幾個小時的撞擊,我找到了解決方案。 希望這對其他人有用。 為了使這個可行,我需要解決三個要點:
我在下面的代碼中突出顯示了這些內容。
首先,為了擺脫異常,我發現子查詢需要一個投影,下面突出顯示。 我剛剛對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.