java 复杂的休眠投影

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12105757/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 07:36:38  来源:igfitidea点击:

Complex Hibernate Projections

javahibernatecriteria

提问by Fauzi Achmad

I want to ask, it is possible that I create query projections and criterion for more than one level deep? I have 2 model classes:

我想问一下,我有可能为不止一个级别的深度创建查询投影和标准吗?我有 2 个模型类:

@Entity  
@Table(name = "person")  
public class Person implements Serializable {
    @Id
    @GeneratedValue
    private int personID;
    private double valueDouble;
    private int valueInt;
    private String name;
    @OneToOne(cascade = {CascadeType.ALL}, orphanRemoval = true)
    @JoinColumn(name="wifeId")
    private Wife wife;
       /*   
        *  Setter Getter    
        */
}


@Entity 
@Table(name = "wife")  
public class Wife implements Serializable {

    @Id
    @GeneratedValue     
    @Column(name="wifeId")
    private int id;
    @Column(name="name")
    private String name;
    @Column(name="age")
    private int age;            
    /*
     *  Setter Getter
     */       
}

My Criteria API :

我的标准 API :

ProjectionList projections = Projections.projectionList(); 
projections.add(Projections.property("this.personID"), "personID");
projections.add(Projections.property("this.wife"), "wife");
projections.add(Projections.property("this.wife.name"), "wife.name");

Criteria criteria = null; 
criteria = getHandlerSession().createCriteria(Person.class); 
criteria.createCriteria("wife", "wife", JoinType.LEFT.ordinal()); 
criterion = Restrictions.eq("wife.age", 19);  
criteria.add(criterion); 
criteria.setProjection(projections);
criteria.setResultTransformer(Transformers.aliasToBean(Person.class)); 
return criteria.list();

and I hope, I can query Person, with specified criteria for wife property, and specified return resultSet. so i used Projections for getting specified return resultSet

我希望,我可以查询 Person,指定妻子属性的条件,并指定返回结果集。所以我使用 Projections 来获取指定的返回结果集

I want personID, name(Person), name(Wife) will returned. how API i must Use, i more prefer use Hibernate Criteria API.

我想要 personID, name(Person), name(Wife) 将返回。我必须如何使用 API,我更喜欢使用 Hibernate Criteria API。

This time, I used code above for getting my expected result, but it will throw Exception with error message : Exception in thread "main" org.hibernate.QueryException: could not resolve property: wife.name of: maladzan.model.Person, and whether my Restrictions.eq("wife.age", 19);is correct for getting person which has wife with 19 as her age value ?

这一次,我用上面得到我预期的结果代码,但它会抛出异常与错误消息: Exception in thread "main" org.hibernate.QueryException: could not resolve property: wife.name of: maladzan.model.Person和我是否Restrictions.eq("wife.age", 19);是获得具有妻子19她的年龄价值的人是否正确?

Thanks

谢谢

回答by Firo

AFAIK it is not possible to project more than one level deep with aliastobean transformer. Your options are

AFAIK 不可能使用 aliastobean 转换器投射超过一层的深度。你的选择是

  • create a flattened Data Transfer Object (DTO)
  • fill the resulting Person in memory yourself
  • implement your own resulttransformer (similar to option 2)
  • 创建扁平化数据传输对象 (DTO)
  • 自己在记忆中填充由此产生的 Person
  • 实现您自己的 resulttransformer(类似于选项 2)

option 1 looks like this:

选项 1 如下所示:

Criteria criteria = getHandlerSession().createCriteria(Person.class)
    .createAlias("wife", "wife", JoinType.LEFT.ordinal())
    .add(Restrictions.eq("wife.age", 19)); 
    .setProjection(Projections.projectionList()
        .add(Projections.property("personID"), "personID")
        .add(Projections.property("name"), "personName")
        .add(Projections.property("wife.name"), "wifeName"));
    .setResultTransformer(Transformers.aliasToBean(PersonWifeDto.class));

return criteria.list();

回答by Sami Andoni

I wrote the ResultTransformer, that does this exactly. It's name is AliasToBeanNestedResultTransformer, check it out on github.

我写的ResultTransformer,正是这样做的。它的名字是AliasToBeanNestedResultTransformer,在github上查看。

回答by whitestryder

Thanks Sami Andoni. I was able to use your AliasToBeanNestedResultTransformer with a minor modification to suit my situation. What I found was that the nested transformer did not support the scenario where the field is in a super class so I enhanced it to look for fields up to 10 levels deep in the class inheritance hierarchy of the class you're projecting into:

谢谢萨米·安多尼。我能够使用您的 AliasToBeanNestedResultTransformer 并稍作修改以适应我的情况。我发现嵌套转换器不支持字段位于超类中的场景,因此我对其进行了增强,以在您投射到的类的类继承层次结构中查找最多 10 层的字段:

    public Object transformTuple(Object[] tuple, String[] aliases) {

        ...


                if (alias.contains(".")) {
                    nestedAliases.add(alias);

                    String[] sp = alias.split("\.");
                    String fieldName = sp[0];
                    String aliasName = sp[1];

                    Class<?> subclass = getDeclaredFieldForClassOrSuperClasses(resultClass, fieldName, 1);
...
}

Where getDeclaredFieldForClassOrSuperClasses() is defined as follows:

其中 getDeclaredFieldForClassOrSuperClasses() 定义如下:

private Class<?> getDeclaredFieldForClassOrSuperClasses(Class<?> resultClass, String fieldName, int level) throws NoSuchFieldException{
    Class<?> result = null;
    try {
        result = resultClass.getDeclaredField(fieldName).getType();
    } catch (NoSuchFieldException e) {
        if (level <= 10){
        return getDeclaredFieldForClassOrSuperClasses(
                resultClass.getSuperclass(), fieldName, level++);
        } else {
            throw e;
        }
    }
    return result;
}

My Hibernate projection for this nested property looked like this:

我对此嵌套属性的 Hibernate 投影如下所示:

Projections.projectionList().add( Property.forName("metadata.copyright").as("productMetadata.copyright"));

and the class I am projecting into looks like this:

我正在投射的课程如下所示:

public class ProductMetadata extends AbstractMetadata {
...
}

public abstract class AbstractMetadata {
...   
   protected String copyright;
...
}

回答by Jubin Patel

Instead of creating Data Transfer Object (DTO)
In projectionlistmake below changes and it will work for you.

而不是创建Data Transfer Object (DTO)
In projectionlistmake 下面的更改,它将为您工作。

    ProjectionList projections = Projections.projectionList(); 
    projections.add(Projections.property("person.personID"), "personID");
    projections.add(Projections.property("person.wife"), "wife");
    projections.add(Projections.property("wife.name"));

    Criteria criteria = null; 
    criteria = getHandlerSession().createCriteria(Person.class,"person").createAlias("person.wife", "wife"); 
    criterion = Restrictions.eq("wife.age", 19);  
    criteria.add(criterion); 
    criteria.setProjection(projections);
    criteria.setResultTransformer(Transformers.aliasToBean(Person.class)); 
    return criteria.list();