收集值的Hibernate标准 [英] Hibernate criteria on collection values

查看:91
本文介绍了收集值的Hibernate标准的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图用Hibernate来组合一个复杂的查询。我一直倾向于Criteria,但我开始怀疑这是不可能的,所以任何建议都会有帮助。



我有一个像下面这样的实体结构:

  public class Attribute {
private Integer id;
私人字符串名称;
private Set< Value>值;
}

公共类实例{
私有整数ID;
private int instanceRef;
private Set< Value>值;
}

public class Value {
private Integer id;
私有属性属性;
private String localAttributeName;
私有实例实例;
私有字符串值;

$ / code>

这些实体与您预期的相关:

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

现在,我希望能够获取一组属性/值对(字符串)并查找包含它们的所有 的所有实例。在Value中,只有一个属性和localAttributeName是非空的,所以属性名称可以匹配localAttributeName或attribute.name。最后一件事情复杂化了,Value上的唯一索引是on(instance,attribute,value)或(instance,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);

//用valueCrit

做一些事情b crit.add(Subqueries.exists(valueCrit));
}
return crit.list();
}

基于我所做的研究,我为此尝试过做一些部分是:

  //这只会检查localAttributeName而不检查attribute.name。 
//没关系 - 一旦我让剩下的工作,我可以弄清楚。
valueCrit.add(Restrictions.eq(localAttributeName,entry.getKey());
valueCrit.add(Restrictions.eq(value,entry.getValue());
valueCrit.add(Restrictions.eqProperty(v.instance_id,i.id));

但是这引发了下面的异常,我怀疑是告诉我我不能用Criteria做到这一点,但是我很想学习另外的东西:

在org.hibernate.loader.criteria.CriteriaQueryTranslator.getProjectedTypes 显示java.lang.NullPointerException 
(CriteriaQueryTranslator.java:341)

最好的方法是做什么? 我在数小时后就想出了解决方案,希望这对其他人有用处。我需要解决以下三个主要问题:


  1. 添加投影

  2. 创建合适的连接 正确地映射子查询返回主标准

我在下面的代码中突出显示了每一个。



首先,为了摆脱这个异常,我发现子查询需要一个投影,下面突出显示。我只是在Instance的id属性上做了一个投影。其次,为了获得连接,我使用Criteria.createCriteria()方法创建了一个左外层加入。因为我在连接的不同级别有多个条件,所以我必须分别保存连接的Criteria和附加表达式。这让我在子查询中执行OR表达式。

最后,我必须添加一个eqProperty()子句来将子查询映射回主标准。就像它需要在结果SQL中一样,我使用了:instance.id = i.id.因为我已经将Instance Criteria映射到i,并将此子句添加到Value Criteria中,所以将其转换为SQL:v.instance_id = i.id。



以下是工作代码:

  public List< Instance> getMatchingInstances(Map< String,String> attrValues){
Criteria crit = session.createCriteria(Instance.class,i); (Map.Entry< String,String>条目:attrValues){
字符串attrName = entry.getKey();
String val = entry.getValue();

//创建子查询
DetachedCriteria valueCrit = DetachedCriteria.forClass(Value.class,v);

//加入属性对象(左外连接)
DetachedCriteria attrCrit =
valueCrit.createCriteria(attribute,CriteriaSpecification.LEFT_JOIN);

//将OR语句放在属性加入的标准上。
Criterion localAttr = Restrictions.eq(v.localAttributeName,attrName);
Criterion globalAttr = Restrictions.eq(name,attrName);
attrCrit.add(Restrictions.or(localAttr,globalAttr));

//子查询条件上的简单列平等。
valueCrit.add(Restrictions.eq(value,val));

//将子查询映射回外部查询。
valueCrit.add(Restrictions.eqProperty(instance.id,i.id));

//添加缺失的投影。
valueCrit.setProjection(Projections.property(id));

//将此子查询添加到外部查询中。
crit.add(Subqueries.exists(valueCrit));
}
return crit.list();
}


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?

解决方案

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:

  1. Add a Projection
  2. Create the proper joins
  3. Properly map the subquery back to the main criteria

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:

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

这篇关于收集值的Hibernate标准的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆