复杂的 Hibernate 投影

5

我想询问一下,是否可以创建多个级别深度的查询投影和条件? 我有两个模型类:

@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
     */       
}

我的 Criteria 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();

我希望您能查询具有指定妻子属性和指定返回结果集的Person。因此,我使用Projections获取指定的返回结果集。 我希望返回personID、name(Person)和name(Wife)。请问我应该使用哪个API?我更喜欢使用Hibernate Criteria API。
这次,我使用了上述代码来获得我期望的结果,但它会抛出异常并显示错误消息:“Exception in thread "main" org.hibernate.QueryException: could not resolve property: wife.name of: maladzan.model.Person”,我的“Restrictions.eq("wife.age", 19);”是否正确,以便获取其妻子年龄为19岁的人?
谢谢
4个回答

6
据我所知,使用aliastobean转换器无法进行多层级投影。你的选择有:
  • 创建一个扁平化的数据传输对象(DTO)
  • 手动在内存中填充结果实体(Person)
  • 实现自己的resulttransformer(类似于选项2)
选项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();

5
我写了一个名为AliasToBeanNestedResultTransformerResultTransformer,它可以实现这个功能。访问github查看详情。

嗨,Sami Andoni。我使用了您的AliasToBeanNestedResultTransformer来创建嵌套对象,我确实得到了嵌套对象作为嵌套对象,但是我有一个小问题。我打算只获取嵌套对象中的特定字段,父对象中的少数字段,但结果是父对象中的所有字段和嵌套对象中的所有字段作为嵌套对象。我不知道您的自定义转换器是否能够获取特定字段,是否可能仅获取嵌套对象中的特定字段作为嵌套对象? - The Coder
你解决了这个问题吗? - Jatin Malwal
@JatinMalwal 什么问题? - Sami Andoni
我正在尝试使用AliasToBeanNestedResultTransformer来获取特定字段,但是当我尝试使用AliasedTupleSubsetResultTransformer时,它无法找到。 - Jatin Malwal

1
感谢Sami Andoni。我能够使用您的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);
...
}

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

我对这个嵌套属性的Hibernate投影如下:

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

我要投影的类别看起来像这样:

public class ProductMetadata extends AbstractMetadata {
...
}

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

你写了一个支持OneToMany(集合)的NestedTransformer吗? - The Coder
@Sangdol 写了这个代码,我只是增强了它的功能,使其支持从基类向上投影到 10 级深度的字段。 - whitestryder

-1

不要创建 Data Transfer Object (DTO)
projectionlist 中进行以下更改,它将适用于您。

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

不工作是一个不同的词,提供您的Hibernate版本和完整查询。 - Jubin Patel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接