Hibernate 关于集合值的标准
我正在尝试使用 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
现在,我希望能够采用一组属性/值对(字符串)并找到包含 all< 的所有实例/em> 其中。 Value 中只有 attribute 和 localAttributeName 之一为非空,因此属性名称可以匹配 localAttributeName 或 attribute.name。最后让事情变得复杂的是,Value 上的唯一索引位于 (instance, attribute, value) 或 (instance, localAttributeName, value) 上——也就是说,在一个 Instance 内,任何给定的 Attribute 都可以有多个 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"));
但这引发了下面的异常,我怀疑它告诉我我不能用 Criteria 来做到这一点,但我很想了解其他方面的知识:
java.lang.NullPointerException
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getProjectedTypes(CriteriaQueryTranslator.java:341)
这样做的最佳方法是什么?
I'm trying to put together a complicated query using Hibernate. I've been leaning toward Criteria, but I'm beginning to suspect it's not possible, and so any suggestions would be helpful.
I have an entity structure like the following:
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;
}
These entities are related as you'd expect:
value.attribute_id --> attribute.id
value.instance_id --> instance.id
Now, I would like to be able to take a set of attribute/value pairs (Strings) and find all instances that contain all of them. In Value, only one of attribute and localAttributeName are non-null, so the attribute name may match either localAttributeName or attribute.name. And to complicate things one last time, the unique index on Value is on (instance, attribute, value) or (instance, localAttributeName, value) -- that is, within an Instance, any given Attribute may have multiple Values.
This is what I have so far:
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();
}
Based on the research I've done, what I've tried for that Do something section is:
// 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"));
But this throws the exception below, which I suspect is telling me I can't do this with Criteria, but I'd love to learn otherwise:
java.lang.NullPointerException
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getProjectedTypes(CriteriaQueryTranslator.java:341)
What would be the best way to go about doing this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
经过几个小时的研究,我找到了解决方案。希望这对其他人有用。为了使这一点可行,我需要解决三个要点:
我在下面的代码中突出显示了其中的每一个。
首先,为了消除异常,我发现子查询需要一个投影,如下突出显示。我刚刚对 Instance 的“id”属性进行了投影。
其次,为了获得连接,我使用 Criteria.createCriteria() 方法创建左外连接。因为我在连接的不同级别有多个条件,所以我必须保存连接的条件并分别向它们附加表达式。这让我可以在子查询中执行 OR 表达式。
最后,我必须添加一个 eqProperty() 子句以将子查询映射回主条件。正如生成的 SQL 中需要的那样,我使用了:instance.id = i.id。因为我已经将实例条件映射到“i”,并将此子句添加到值条件中,所以这会转换为 SQL:v.instance_id = i.id。
这是工作代码:
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:
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: