`
onewayjp
  • 浏览: 2719 次
  • 性别: Icon_minigender_1
  • 来自: 海南\东北
最近访客 更多访客>>
文章分类
社区版块
存档分类

一个关于Hibernate的优化实例:从HQL到QBC,从QBC到QBE,再到“增强的”QBE

阅读更多
先解释一下标题的含义:为了实现一个组合条件查询,先是使用HQL书写,然后改用Query by Criteria方式,再尝试Query by Example,最后自己实现了一个增强的Example类来解决问题。

关于此问题的起源请阅读我以前的一个帖子:http://www.iteye.com/post/523791。在该帖子中已经实现了从HQL到QBC的转变,在这里就不再重复了。

在上一个帖子中没有模型类Product及Category的代码,为了方便讨论补充如下:

Java代码
public class Category {  
  private Long id;  
  private String name; //类别名称  
 
  //Other code omitted  
}  
 
public class Product {  
  private Long id;  
  private String name;       //商品名称  
  private Category category; //商品类别  
  private Date expDate;      //有效期  
  private Float price;       //单价  
 
  //Other code omitted  


public class Category {
  private Long id;
  private String name; //类别名称

  //Other code omitted
}

public class Product {
  private Long id;
  private String name;       //商品名称
  private Category category; //商品类别
  private Date expDate;      //有效期
  private Float price;       //单价

  //Other code omitted
}


从前一个帖子中可以看到,使用QBC后代码有所减少,但还是得把构造查询条件的代码写死,这非常不爽。重读了《Java Persistence with Hibernate》一书,发觉QBE是个好东东,于是尝试用改造代码如下:

Java代码
public List<Product> getProducts(Product product) {  
    final Example exampleProduct =  
      Example.create(product).  
        enableLike(MatchMode.ANYWHERE).  
                   excludeZeroes();  
 
    return (List<Product>) getHibernateTemplate().execute(  
      new HibernateCallback() {  
        public Object doInHibernate(Session session) throws HibernateException {  
            Criteria crit =  
              session.createCriteria(Product.class).  
              add(exampleProduct);  
            return crit.list();  
        }  
      }  
    );  


public List<Product> getProducts(Product product) {
final Example exampleProduct =
  Example.create(product).
    enableLike(MatchMode.ANYWHERE).
                    excludeZeroes();

return (List<Product>) getHibernateTemplate().execute(
  new HibernateCallback() {
    public Object doInHibernate(Session session) throws HibernateException {
    Criteria crit =
      session.createCriteria(Product.class).
      add(exampleProduct);
    return crit.list();
    }
  }
);
}


代码非常简洁啊!我只要new一个Product实例,然后把要查询的条件值赋值到相应到属性上,如果某项条件未指定则相应的属性保留为默认的空值,将该实例传递给上面的getProducts方法,就能得到需要的结果了。超爽!

但是我却没办法把这段代码用在产品中,这是因为QBE有着严重的局限性:
1.不能查询指定在关联对象的属性上的条件。比如我想仅列出商品类别名称包括xyz的商品,代码如下:

Java代码
Category category = new Category();  
category.setName("xyz");  
Product product = new Product();  
product.setCategory(category);  
 
List<Product> products = getProducts(product); 

Category category = new Category();
category.setName("xyz");
Product product = new Product();
product.setCategory(category);

List<Product> products = getProducts(product);


运行这段代码会列出所有的商品。

2.除了字符串条件可以调用enableLike()方法改用模糊查询外,其它数据类型的条件都只能等值比较。比如我无法查询所有有效的商品(有效期≥当前日期)。

难道就没有办法了吗?经过一番搜索,终于在Hibernate的官网论坛上找到一篇文章:http://forum.hibernate.org/viewtopic.php?t=942872。在该文章中,Dencel写了一个AssociationExample,经过大家的完善,终于解决了查询指定在关联对象的属性上的条件的问题。其主要的奥妙在于:

Java代码
//Hibernate的原版Example  
//如果属性类型是关联的实体,则忽略  
    private boolean isPropertyIncluded(Object value, String name, Type type) {  
        return !excludedProperties.contains(name) &&  
            !type.isAssociationType() &&  
            selector.include(value, name, type);  
    }  
 
//改版的AssociationExample  
  private boolean includeAssociations = true;  
 
  public boolean isIncludeAssociations()  
  {  
    return includeAssociations;  
  }  
 
  public void setIncludeAssociations(boolean includeAssociations)  
  {  
    this.includeAssociations = includeAssociations;  
  }  
 
//如果属性类型是关联的实体,且该关联是一对一或多对一,且includeAssociations为true,则包括该属性  
  private boolean isPropertyIncluded(Object value, String name, Type type) {  
    return 
      !excludedProperties.contains(name) &&  
      selector.include(value, name, type) &&  
      (!type.isAssociationType() ||  
        (type.isAssociationType() &&  
          includeAssociations &&  
          !type.isCollectionType()));  
  } 

//Hibernate的原版Example
//如果属性类型是关联的实体,则忽略
private boolean isPropertyIncluded(Object value, String name, Type type) {
return !excludedProperties.contains(name) &&
!type.isAssociationType() &&
selector.include(value, name, type);
}

//改版的AssociationExample
  private boolean includeAssociations = true;

  public boolean isIncludeAssociations()
  {
    return includeAssociations;
  }

  public void setIncludeAssociations(boolean includeAssociations)
  {
    this.includeAssociations = includeAssociations;
  }

//如果属性类型是关联的实体,且该关联是一对一或多对一,且includeAssociations为true,则包括该属性
  private boolean isPropertyIncluded(Object value, String name, Type type) {
    return
      !excludedProperties.contains(name) &&
      selector.include(value, name, type) &&
      (!type.isAssociationType() ||
        (type.isAssociationType() &&
          includeAssociations &&
          !type.isCollectionType()));
  }


解决了前面提到的第一个问题,第二个问题又怎么办呢?我想到一个办法:如果某个条件要使用其它的比较方式(比如大于等于),提供一个方法让用户为该属性指定比较方法,对于其它属性仍采用缺省的查询/比较方法:

Java代码
//Hibernate原版的Example  
  protected void appendPropertyCondition(  
    String propertyName,  
    Object propertyValue,  
    Criteria criteria,  
    CriteriaQuery cq,  
    StringBuffer buf)  
  throws HibernateException {  
    Criterion crit;  
    if ( propertyValue!=null ) {  
//当属性值不为空时,如果是字符串且指定为模糊查询,则使用模糊查询,否则使用等值比较  
      boolean isString = propertyValue instanceof String;  
      SimpleExpression se = ( isLikeEnabled && isString ) ?  
        Restrictions.like(propertyName, propertyValue) :  
        Restrictions.eq(propertyName, propertyValue);  
      crit = ( isIgnoreCaseEnabled && isString ) ?  
        se.ignoreCase() : se;  
    }  
    else {  
      crit = Restrictions.isNull(propertyName);  
    }  
    String critCondition = crit.toSqlString(criteria, cq);  
    if ( buf.length()>1 && critCondition.trim().length()>0 ) buf.append(" and ");  
    buf.append(critCondition);  
  }  
 
 
//增强后的EnhancedExample  
  private static final RestrictionHolder holder = new DefaultRestrictionHolder();  
 
  /** 
   * Restriction strategy definitions 
   */ 
  public static enum RestrictionStrategy {eq, ne, gt, lt, ge, le}  
 
  /** 
   * Restriction strategy holder for the query criteria 
   */ 
  public static interface RestrictionHolder {  
    /** 
     * Set a restriction strategy for a POJO's property 
     */ 
    public RestrictionHolder set(String propertyName, RestrictionStrategy strategy);  
 
    /** 
     * Get the restriction strategy of the property 
     */ 
    public RestrictionStrategy get(String propertyName);  
  }  
 
  static final class DefaultRestrictionHolder implements RestrictionHolder {  
        private Map<String, RestrictionStrategy> strategies = new HashMap<String, RestrictionStrategy>();  
 
    public RestrictionHolder set(String propertyName, RestrictionStrategy strategy) {  
      strategies.put(propertyName, strategy);  
      return this;  
    }  
 
    public RestrictionStrategy get(String propertyName) {  
      return strategies.get(propertyName);  
    }  
  }  
 
  /** 
   * Get the restriction strategy holder 
   */ 
  public RestrictionHolder getRestrictionHolder() {  
    return holder;  
  }  
 
  protected void appendPropertyCondition(  
    String propertyName,  
    Object propertyValue,  
    Criteria criteria,  
    CriteriaQuery cq,  
    StringBuffer buf)  
  throws HibernateException {  
    Criterion crit;  
    if ( propertyValue!=null ) {  
//当属性值不为空时,如果为该属性指定了比较条件,则使用指定的比较条件  
      RestrictionStrategy strategy = holder.get(propertyName);  
      if ( strategy != null ) {  
    switch(strategy) {  
        //case eq: crit = Restrictions.eq(propertyName, propertyValue);  
        case ne: crit = Restrictions.ne(propertyName, propertyValue); break;  
        case gt: crit = Restrictions.gt(propertyName, propertyValue); break;  
        case lt: crit = Restrictions.lt(propertyName, propertyValue); break;  
        case ge: crit = Restrictions.ge(propertyName, propertyValue); break;  
        case le: crit = Restrictions.le(propertyName, propertyValue); break;  
        default: crit = Restrictions.eq(propertyName, propertyValue);  
        };  
      }  
      else {  
//否则使用默认的比较条件:如果是字符串且指定为模糊查询,则使用模糊查询,否则使用等值比较  
    boolean isString = propertyValue instanceof String;  
    SimpleExpression se = ( isLikeEnabled && isString ) ?  
      Restrictions.like(propertyName, propertyValue) :  
      Restrictions.eq(propertyName, propertyValue);  
    crit = ( isIgnoreCaseEnabled && isString ) ?  
      se.ignoreCase() : se;  
      }  
    }  
    else {  
      crit = Restrictions.isNull(propertyName);  
    }  
    String critCondition = crit.toSqlString(criteria, cq);  
    if ( buf.length()>1 && critCondition.trim().length()>0 ) buf.append(" and ");  
    buf.append(critCondition);  
  } 

//Hibernate原版的Example
  protected void appendPropertyCondition(
    String propertyName,
    Object propertyValue,
    Criteria criteria,
    CriteriaQuery cq,
    StringBuffer buf)
  throws HibernateException {
    Criterion crit;
    if ( propertyValue!=null ) {
//当属性值不为空时,如果是字符串且指定为模糊查询,则使用模糊查询,否则使用等值比较
      boolean isString = propertyValue instanceof String;
      SimpleExpression se = ( isLikeEnabled && isString ) ?
        Restrictions.like(propertyName, propertyValue) :
        Restrictions.eq(propertyName, propertyValue);
      crit = ( isIgnoreCaseEnabled && isString ) ?
        se.ignoreCase() : se;
    }
    else {
      crit = Restrictions.isNull(propertyName);
    }
    String critCondition = crit.toSqlString(criteria, cq);
    if ( buf.length()>1 && critCondition.trim().length()>0 ) buf.append(" and ");
    buf.append(critCondition);
  }


//增强后的EnhancedExample
  private static final RestrictionHolder holder = new DefaultRestrictionHolder();

  /**
   * Restriction strategy definitions
   */
  public static enum RestrictionStrategy {eq, ne, gt, lt, ge, le}

  /**
   * Restriction strategy holder for the query criteria
   */
  public static interface RestrictionHolder {
  /**
  * Set a restriction strategy for a POJO's property
  */
    public RestrictionHolder set(String propertyName, RestrictionStrategy strategy);

    /**
     * Get the restriction strategy of the property
     */
    public RestrictionStrategy get(String propertyName);
  }

  static final class DefaultRestrictionHolder implements RestrictionHolder {
private Map<String, RestrictionStrategy> strategies = new HashMap<String, RestrictionStrategy>();

    public RestrictionHolder set(String propertyName, RestrictionStrategy strategy) {
      strategies.put(propertyName, strategy);
      return this;
    }

    public RestrictionStrategy get(String propertyName) {
      return strategies.get(propertyName);
    }
  }

  /**
   * Get the restriction strategy holder
   */
  public RestrictionHolder getRestrictionHolder() {
    return holder;
  }

  protected void appendPropertyCondition(
    String propertyName,
    Object propertyValue,
    Criteria criteria,
    CriteriaQuery cq,
    StringBuffer buf)
  throws HibernateException {
    Criterion crit;
    if ( propertyValue!=null ) {
//当属性值不为空时,如果为该属性指定了比较条件,则使用指定的比较条件
      RestrictionStrategy strategy = holder.get(propertyName);
      if ( strategy != null ) {
  switch(strategy) {
    //case eq: crit = Restrictions.eq(propertyName, propertyValue);
    case ne: crit = Restrictions.ne(propertyName, propertyValue); break;
    case gt: crit = Restrictions.gt(propertyName, propertyValue); break;
    case lt: crit = Restrictions.lt(propertyName, propertyValue); break;
    case ge: crit = Restrictions.ge(propertyName, propertyValue); break;
    case le: crit = Restrictions.le(propertyName, propertyValue); break;
    default: crit = Restrictions.eq(propertyName, propertyValue);
        };
      }
      else {
//否则使用默认的比较条件:如果是字符串且指定为模糊查询,则使用模糊查询,否则使用等值比较
boolean isString = propertyValue instanceof String;
SimpleExpression se = ( isLikeEnabled && isString ) ?
  Restrictions.like(propertyName, propertyValue) :
  Restrictions.eq(propertyName, propertyValue);
crit = ( isIgnoreCaseEnabled && isString ) ?
  se.ignoreCase() : se;
      }
    }
    else {
      crit = Restrictions.isNull(propertyName);
    }
    String critCondition = crit.toSqlString(criteria, cq);
    if ( buf.length()>1 && critCondition.trim().length()>0 ) buf.append(" and ");
    buf.append(critCondition);
  }


于是前面getProducts方法只需要简单修改一下:

Java代码
public List<Product> getProducts(Product product) {  
    //改用EnhancedExample来允许关联对象的条件查询  
    final EnhancedExample exampleProduct =  
      EnhancedExample.create(product).  
        enableLike(MatchMode.ANYWHERE).  
                   excludeZeroes();  
      //指定expDate属性使用大于等于比较方法  
    exampleProduct.getRestrictionHolder().  
      set("expDate", EnhancedExample.RestrictionStrategy.ge);  
 
    return (List<Product>) getHibernateTemplate().execute(  
      new HibernateCallback() {  
        public Object doInHibernate(Session session) throws HibernateException {  
            Criteria crit =  
              session.createCriteria(Product.class).  
              add(exampleProduct);  
            return crit.list();  
        }  
      }  
    );  


public List<Product> getProducts(Product product) {
//改用EnhancedExample来允许关联对象的条件查询
final EnhancedExample exampleProduct =
  EnhancedExample.create(product).
    enableLike(MatchMode.ANYWHERE).
                    excludeZeroes();
  //指定expDate属性使用大于等于比较方法
exampleProduct.getRestrictionHolder().
  set("expDate", EnhancedExample.RestrictionStrategy.ge);

return (List<Product>) getHibernateTemplate().execute(
  new HibernateCallback() {
    public Object doInHibernate(Session session) throws HibernateException {
    Criteria crit =
      session.createCriteria(Product.class).
      add(exampleProduct);
    return crit.list();
    }
  }
);
}


经过以上改进,QBE的实用性大大提高,能够真正解决较大多数的组合查询问题。

已知的问题:以上“增强的”QBE还无法解决范围查询(比如价格在0到1000之间),这是因为一个属性只能携带一个值(你不可能指定两个值给Product.price属性)。这种情况下需要修改getProducts方法,增加参数把价格范围传递进来,再以QBC方式把相应的条件加到crit变量上。范例代码就不再给出了。

完整的EnhancedExample源码请见附件。
EnhancedExample.zip (3 KB)
描述: 完整的EnhancedExample源码
下载次数: 219
分享到:
评论
1 楼 zzx5286 2008-10-08  
不错,谢谢!

相关推荐

    Hibernate-HQL-QBC-QBE

    1. Hibernate:Hibernate是一个开源的对象关系映射(ORM)框架,它允许开发者在Java应用中使用面向对象的方式来操作数据库。它通过提供一套API和元数据,将Java类与数据库表进行映射,从而简化了数据库操作。 2. ...

    Hibernate_QBC和Hibernate_QBE

    Query By Example(QBE)则是通过实例化一个对象,并设置其属性值,然后将这个对象作为查询的模板,Hibernate会根据对象的属性值生成对应的WHERE子句进行查询。这种方式在处理简单查询时非常直观,但当查询条件变得...

    QBC QBE查询

    QBC是Hibernate提供的一个高级查询接口,它允许开发者通过实例化Criteria对象来构造查询条件,从而实现动态查询。QBC的优点在于它可以更好地映射到对象模型,避免了直接编写SQL语句,提高了代码的可读性和可维护性。...

    Hibernate教程21_HIbernate查询

    3. **s2sh_relation22_QBE.zip**: QBE(Query By Example)是Hibernate的另一种查询方式,它允许根据一个对象实例来构建查询。这个压缩包可能包含了关于QBE的示例,帮助你理解如何基于对象实例进行查询。 通过学习...

    hibernate3

    2. 查询语言(HQL):Hibernate查询语言,类似于SQL,但面向对象,可以方便地进行复杂查询。 3. Criteria查询:提供动态构建查询的方式,无需预先编写HQL,更灵活。 4. Query By Example(QBE):根据给定对象实例的...

    Hibernate查询方法之探析.doc

    QBE是一种基于实例的查询方式,通过创建一个对象实例作为模板,然后根据该实例的属性进行匹配查询。例如,如果有一个`Student`类的对象`s`,其`giftortoy`字段为"2",那么通过`session.createCriteria(Student.class...

    hibernate查询

    Hibernate 是一个开放源代码的 ORM(对象关系映射)框架,它为 Java 开发者提供了一种高效、高性能的方式将 Java 应用程序中的对象映射到数据库表中。在 Hibernate 中进行查询是其核心功能之一,本文将详细介绍 ...

    hibernate和spring技术难点及其要点总结

    首先,我们来看看Hibernate,这是一个流行的ORM(对象关系映射)框架,它简化了Java应用程序与数据库之间的交互。与传统的JDBC相比,Hibernate提供了一种对象封装的方式,使得开发者可以更专注于业务逻辑,而不是...

    Hibernate中Criteria的用法

    Criteria 查询机制主要包括 QBE(Query By Example)、QBC(Query By Criteria)和 HQL(Hibernate Query Language)三种方式。 QBE(Query By Example) QBE 是一种基于示例的查询机制,它可以根据一个示例对象来...

    java之hibernate和spring技术难点及其要点总结

    - **报表工具之JasperReports**:一个强大的报表生成工具,支持多种数据源并能够生成各种格式的报表。 - **使用Action返回数据生成Excel**:结合Struts2等框架实现将查询结果导出为Excel文件的功能。 - **Struts2 + ...

    hibernate详细笔记.

    Hibernate 是一个开源的对象关系映射框架,它允许开发者将Java对象和关系数据库的数据进行映射,从而简化数据访问。以下是对Hibernate的一些关键知识点的详细解释: 1. **配置**: Hibernate 的配置通常通过`...

    HIBERNATE_QUERY

    QBE允许通过一个实体示例来构建查询条件,从而获取与示例相匹配的对象集合。例如: ```java Cat exampleCat = new Cat(); exampleCat.setName("Tom"); List&lt;Cat&gt; cats = session.createCriteria(Cat.class) .add...

    Hibernate关键知识点大全

    - **Hibernate**:是一个ORM(Object-Relational Mapping)框架,用于将Java对象与数据库表进行映射,简化数据库操作。 - **JPA(Java Persistence API)**:是Java标准的持久化API,提供了一套规范,而Hibernate...

    Hibernate中Criteria的完整用法

    在Java的持久化框架Hibernate中,Criteria API提供了一种动态构造SQL查询的方式,它允许开发者在运行时构建查询,而无需硬编码HQL(Hibernate Query Language)或原生SQL语句。Criteria API提供了丰富的功能,包括但...

    hibernateCriteria的使用.docx

    QBE是通过提供一个对象实例作为模板来执行查询的方法。例如,在给定的代码片段中: ```java Student s = new Student(); s.setGiftOrToy("2"); Criteria cri = session.createCriteria(Student.class); cri.add...

    Hibernate全部知识点

    - 实现树状结构是Hibernate中的一个高级主题,通常涉及到递归查询或使用特定的树结构算法。 #### HQL查询语言 - **Hibernate Query Language (HQL)** 是一种面向对象的查询语言,类似于SQL,但更加灵活和强大。 - *...

    Hibernate查询语言.doc

    本文主要探讨Hibernate中的查询语言,包括面向对象的查询语言HQL,QueryBy Criteria (QBC),Query By Example (QBE),以及原生SQL的使用。 1. **大小写敏感性** HQL查询语句对Java类和属性名称的大小写敏感,而...

Global site tag (gtag.js) - Google Analytics