【问题标题】:How to use a nested property as a named param in a hibernate query?如何在休眠查询中使用嵌套属性作为命名参数?
【发布时间】:2015-04-04 20:50:24
【问题描述】:

我真的想在休眠查询中使用嵌套属性作为命名参数。 所以我可以更优雅地维护我的 param bean。

例如,我想写我的 HQL:

"......
where
    employee.age >= :age.min
  and
    employee.age <= :age.max
  and
    employee.name = :name"

并将所有参数放入一个 paramBean, 在这个 paramBean 中,有一个嵌套的 bean(名为“age”), 并且嵌套的bean有2个属性:min和max。

但问题是:

  1. HQL 语法不支持作为命名参数的嵌套属性: Hibernate 会抛出异常,因为它不允许使用 “。”在参数名称中。
  2. 在 org.hibernate.internal.AbstractQueryImpl.setProperties(Object) 方法,实现代码为:

        Getter getter = ReflectHelper.getGetter( clazz, namedParam );
        Class retType = getter.getReturnType();
        final Object object = getter.get( bean );
    

它在param bean上使用getter方法,所以它无法检索嵌套属性。

我必须在 param bean 中创建很多委托方法来访问嵌套属性:

public int getAgeMin() {
    return this.age.getMin();
}


public int getAgeMax() {
    return this.age.getMax();
}

并像这样编写 HQL:

"......
where
    employee.age >= :ageMin
  and
    employee.age <= :ageMax
  and
    employee.name = :name"

这个问题困扰了我多年。

我终于找到了修复它的方法。

解决办法如下:

  1. 对于问题 (1):在 HQL 中:使用“_”作为“.”的转义字符

HQL 喜欢:

"......
where
    employee.age >= :age_min
  and
    employee.age <= :age_max
  and
    employee.name = :name"
  1. 针对问题(2):编写一些辅助方法,设置所有参数值 为 HQL。

辅助方法的代码是:

private void setParameters(final Query query, final Object paramBean) {
    for (String namedParam : query.getNamedParameters()) {
        try {
            // !!! Fix problem (1) !!!
            // unescape the param name into nested property name
            String nestedPropName = StringUtils.replace(namedParam, "_",
                    ".");

            // !!! Fix problem (2) !!!
            // retrieve the nested property, using Apache Commons BeanUtils
            // see: http://commons.apache.org/proper/commons-beanutils/
            Object paramValue = PropertyUtils.getNestedProperty(paramBean,
                    nestedPropName);

            Class<?> paramType = null;
            if (paramValue != null) {
                paramType = paramValue.getClass();
            }

            if ((paramType != null)
                    && Collection.class.isAssignableFrom(paramType)) {
                query.setParameterList(namedParam,
                        (Collection<?>) paramValue);
            } else if ((paramType != null) && paramType.isArray()) {
                query.setParameterList(namedParam, (Object[]) paramValue);
            } else {
                Type type = this.guessType(paramType);
                query.setParameter(namedParam, paramValue, type);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}


private Type guessType(final Class<?> clazz) throws HibernateException {
    SessionFactoryImplementor sessionFactoryImplementor = (SessionFactoryImplementor) this.sessionFactory;

    String typeName = clazz.getName();
    Type type = sessionFactoryImplementor.getTypeResolver().heuristicType(
            typeName);
    boolean serializable = type != null && type instanceof SerializableType;
    if (type == null || serializable) {
        try {
            sessionFactoryImplementor.getEntityPersister(clazz.getName());
        } catch (MappingException me) {
            if (serializable) {
                return type;
            } else {
                throw new HibernateException(
                        "Could not determine a type for class: " + typeName);
            }
        }
        return this.getSession().getTypeHelper().entity(clazz);
    } else {
        return type;
    }
}

重点是[!!!修复问题 (1) !!!][!!!修复问题 (2) !!!],

所有其他代码都简单地从 org.hibernate.internal.AbstractQueryImpl 复制

【问题讨论】:

    标签: java sql hibernate parameters param


    【解决方案1】:

    改用example query Criteria 怎么样:

    Age age = ...;
    Employee employee = new Employee();
    employee.setAge(age);
    Example example = Example.create(employee);             
    List results = session.createCriteria(Employee.class)
        .add(example)
        .list();
    

    这与嵌套过滤参数尽可能接近。

    【讨论】:

      【解决方案2】:

      为什么我需要这个?

      1. 这不仅是“SELECT”的问题,而且也发生在 “删除”、“更新”

      2. 如果我需要编写一个非常复杂的查询,我更喜欢 HQL,但不是 标准

      3. 嵌套参数 Bean/嵌套属性使我能够维护我的 参数 bean 更优雅。例如,我有一堂课: [ValueRange>],有4个属性:

      public class ValueRange<T extends Comparable<T>> {
        private T min;
      
        private boolean includeMin;
      
        private T max;
      
        private boolean includeMax;
      
      .....
      }
      

      我喜欢用这个容器Bean来呈现所有的ValueRange条件,喜欢:

      where
      ....
          employee.birthDate >= :dateRange.min
        and
          employee.birthDate <= :dateRange.max
      ....
          employee.salary >= :salaryRange.min
        and
          employee.salary <= :salaryRange.max
      
      1. 甚至更复杂的模式:我喜欢:

      (4-1)使用 DepartmentCondition bean 来呈现搜索条件 部门。

      (4-2)使用 EmployeeCondition bean 来呈现 Employee 的搜索条件。

      (4-3)使用 DepartmentCondition 作为 EmployeeCondition 的嵌套 Bean 来呈现员工所在部门的搜索条件

      (4-4)我喜欢这样写HQL:

      where
      ....
          employee.birthDate >= :dateRange.min
        and
          employee.birthDate <= :dateRange.max
      ....
          employee.salary >= :salaryRange.min
        and
          employee.salary <= :salaryRange.max
      ....
          employee.department.code = :department.code
      and
          employee.department.name = :department.name
      

      通过使用这种设计模式,DepartmentCondition bean 可以是一个公共 bean,用于部门查询和员工查询。

      这样我就可以更优雅地维护我的 param bean。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-27
        • 1970-01-01
        • 2013-12-13
        相关资源
        最近更新 更多