収集値に関する冬眠基準
-
27-10-2019 - |
質問
Hibernateを使用して複雑なクエリをまとめようとしています。私は基準に傾いてきましたが、私はそれが不可能であると疑っています。したがって、提案は役に立ちます。
次のようなエンティティ構造があります。
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
今、私は属性/値のペア(文字列)のセットを取得し、含まれるすべてのインスタンスを見つけたいと思っています 全て そのうちの。値では、属性とlocalattributeNameのいずれかのみが非ヌルであるため、属性名はlocalattributeNameまたは属性のいずれかと一致する可能性があります。そして、最後に物事を複雑にするために、値に関する一意のインデックスは(インスタンス、属性、値)または(インスタンス、localattributeName、value)にあります。つまり、インスタンス内で、特定の属性には複数の値がある場合があります。
これは私がこれまでに持っているものです:
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"));
しかし、これは以下の例外を投げかけます。これは、基準でこれを行うことはできないと言っているのではないかと思いますが、そうでなければ学びたいです。
java.lang.NullPointerException
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getProjectedTypes(CriteriaQueryTranslator.java:341)
これを行うための最良の方法は何でしょうか?
解決
数時間の叩きをした後、私は解決策を理解しました。うまくいけば、これは他の人に役立つことです。これを実行可能にするために解決する必要がある3つの主要なポイントがありました。
- 投影を追加します
- 適切な結合を作成します
- サブクエリをメイン基準に適切にマッピングします
これらのそれぞれを以下のコードで強調表示しました。
まず、例外を取り除くために、私は下に強調されているサブクエリに予測が必要であることを発見しました。インスタンスの「ID」プロパティで予測を行いました。
第二に、結合を取得するために、Criteria.createCriteria()メソッドを使用して、左外の結合を作成しました。結合の異なるレベルで複数の条件があったため、結合基準を保存し、式を個別に添付する必要がありました。これにより、私はサブクエリで私や表現をさせました。
最後に、eqproperty()句を追加して、サブクエリをメイン基準にマッピングする必要がありました。結果のSQLにある必要があるように、私は以下を使用しました:instance.id = i.id。私はすでにインスタンス基準を「i」にマッピングしていて、この句を値基準に追加していたため、これは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();
}