【问题标题】:Complex Hibernate Projections复杂的休眠预测
【发布时间】:2012-08-19 19:16:09
【问题描述】:

我想问一下,我创建的查询预测和标准是否有可能超过一层深度? 我有 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
     */       
}

我的标准 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 岁的人?

谢谢

【问题讨论】:

    标签: java hibernate criteria


    【解决方案1】:

    AFAIK 无法使用 aliastobean 变压器投射超过一层的深度。您的选择是

    • 创建平面数据传输对象 (DTO)
    • 自己将生成的 Person 填充到内存中
    • 实现您自己的结果转换器(类似于选项 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();
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 嗨,萨米安多尼。我确实使用您的 AliasToBeanNestedResultTransformer 来创建嵌套对象,我确实将嵌套对象作为嵌套对象,但我有一个小问题。我打算只获取嵌套对象中的特定字段,仅获取父对象中的少数字段,但结果是父对象中的所有字段和嵌套对象中的所有字段作为嵌套对象。我不知道您的自定义转换器是否能够仅获取特定字段,是否可以仅获取嵌套对象中的特定字段作为嵌套对象?
      • 你解决了这个问题吗?
      • @JatinMalwal 什么问题?
      • 我正在尝试使用 AliasToBeanNestedResultTransformer 仅获取特定字段。但是当我尝试做这个时,没有找到 AliasedTupleSubsetResultTransformer。
      【解决方案3】:

      感谢萨米·安多尼。我能够使用您的 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 (Collections)的NestedTransformer吗?
      • @Sangdol 写的,我只是增强了它以支持从基类投影到高达 10 层深度的字段
      【解决方案4】:

      而不是创建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();
      

      【讨论】:

      • 不工作是不同的词,提供你的休眠版本和完整的查询
      猜你喜欢
      • 1970-01-01
      • 2017-08-11
      • 1970-01-01
      • 2013-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-04
      • 1970-01-01
      相关资源
      最近更新 更多