关于集合值的Hibernate标准

我正在尝试使用Hibernate组合一个复杂的查询。 我一直倾向于Criteria,但我开始怀疑它是不可能的,因此任何建议都会有所帮助。

我有一个如下所示的实体结构:

public class Attribute { private Integer id; private String name; private Set values; } public class Instance { private Integer id; private int instanceRef; private Set 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) – 也就是说,在实例中,任何给定的属性都可能有多个值。

这是我到目前为止:

 public List getMatchingInstances(Map attrValues) { Criteria crit = session.createCriteria(Instance.class, "i"); for(Map.Entry 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. 正确地将子查询映射回主要标准

我在下面的代码中突出显示了这些内容。

首先,为了摆脱exception,我发现子查询需要一个投影,下面突出显示。 我刚刚对Instance的“id”属性进行了投影。

其次,为了获得连接,我使用了Criteria.createCriteria()方法来创建左外连接。 因为我在连接的不同级别有多个条件,所以我必须保存连接的Criteria并将表达式分别附加到它们。 这让我在子查询中做我的OR表达式。

最后,我必须添加一个eqProperty()子句来将子查询映射回主Criteria。 就像它需要在生成的SQL中一样,我使用了:instance.id = i.id. 因为我已经将实例标准映射到“i”并且正在将此子句添加到Value Criteria,这转换为SQL:v.instance_id = i.id.

这是工作代码:

 public List getMatchingInstances(Map attrValues) { Criteria crit = session.createCriteria(Instance.class, "i"); for(Map.Entry 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(); }