Hibernate.org Community Documentation

- 上一页
- 下一页

## 第 17 章 条件查询（Criteria Queries）

具有一个直观的、可扩展的条件查询 API 是 Hibernate 的特色。

## 17.1. 创建一个 `Criteria` 实例

`org.hibernate.Criteria`接口表示特定持久类的一个查询。`Session` 是 `Criteria` 实例的工厂。

    Criteria crit = sess.createCriteria(Cat.class);

    crit.setMaxResults(50);

    List cats = crit.list();

## 17.2. 限制结果集内容

一个单独的查询条件是 `org.hibernate.criterion.Criterion` 接口的一个实例。`org.hibernate.criterion.Restrictions` 类定义了获得某些内置 `Criterion` 类型的工厂方法。

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "Fritz%") )

        .add( Restrictions.between("weight", minWeight, maxWeight) )

        .list();

约束可以按逻辑分组。

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "Fritz%") )

        .add( Restrictions.or(

            Restrictions.eq( "age", new Integer(0) ),

            Restrictions.isNull("age")

        ) )

        .list();

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )

        .add( Restrictions.disjunction()

            .add( Restrictions.isNull("age") )

            .add( Restrictions.eq("age", new Integer(0) ) )

            .add( Restrictions.eq("age", new Integer(1) ) )

            .add( Restrictions.eq("age", new Integer(2) ) )

        ) )

        .list();

Hibernate 提供了相当多的内置 criterion 类型（`Restrictions` 子类），但是尤其有用的是可以允许你直接使用 SQL。

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )

        .list();

`{alias}` 占位符应当被替换为被查询实体的列别名。

`Property` 实例是获得一个条件的另外一种途径。你可以通过调用 `Property.forName()` 创建一个 `Property`：

    Property age = Property.forName("age");

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.disjunction()

            .add( age.isNull() )

            .add( age.eq( new Integer(0) ) )

            .add( age.eq( new Integer(1) ) )

            .add( age.eq( new Integer(2) ) )

        ) )

        .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )

        .list();

## 17.3. 结果集排序

你可以使用 `org.hibernate.criterion.Order` 来为查询结果排序。

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "F%")

        .addOrder( Order.asc("name") )

        .addOrder( Order.desc("age") )

        .setMaxResults(50)

        .list();

    List cats = sess.createCriteria(Cat.class)

        .add( Property.forName("name").like("F%") )

        .addOrder( Property.forName("name").asc() )

        .addOrder( Property.forName("age").desc() )

        .setMaxResults(50)

        .list();

## 17.4. 关联

通过使用 `createCriteria()` 对关联进行导航，你可以指定相关实体的约束。

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "F%") )

        .createCriteria("kittens")

            .add( Restrictions.like("name", "F%") )

        .list();

注意第二个 `createCriteria()` 返回一个新的 `Criteria` 实例，该实例引用 `kittens` 集合中的元素。

接下来，替换形态在某些情况下也是很有用的。

    List cats = sess.createCriteria(Cat.class)

        .createAlias("kittens", "kt")

        .createAlias("mate", "mt")

        .add( Restrictions.eqProperty("kt.name", "mt.name") )

        .list();

（`createAlias()` 并不创建一个新的 `Criteria` 实例。）

`Cat` 实例所保存的之前两次查询所返回的 kittens 集合是 没有被条件预过滤的。如果你希望只获得符合条件的 kittens，你必须使用 `ResultTransformer`。

    List cats = sess.createCriteria(Cat.class)

        .createCriteria("kittens", "kt")

            .add( Restrictions.eq("name", "F%") )

        .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)

        .list();

    Iterator iter = cats.iterator();

    while ( iter.hasNext() ) {

        Map map = (Map) iter.next();

        Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);

        Cat kitten = (Cat) map.get("kt");

    }

此外，你可以用一个 left outer join 来操纵结果集：

                    List cats = session.createCriteria( Cat.class )
                           .createAlias("mate", "mt", Criteria.LEFT_JOIN, Restrictions.like("mt.name", "good%") )
                           .addOrder(Order.asc("mt.age"))
                           .list();

这将返回配偶的名字以 "good" 起始的所有 `Cat`，并根据其配偶的年龄进行排序。当需要在返回复杂/大型结果集前进行排序或限制、在多个查询必须执行且结果通过 Java 在内存里组合从而删除许多实例时，这很有用。

如果没有这个功能，那么没有配偶的猫就需要在一次查询里进行加载。

第二个查询将需要获取配偶名以 "good" 起始并按照配偶年龄排序的猫。

第三点，列表需要在内存中进行手工联合。

## 17.5. 动态关联抓取

你可以使用 `setFetchMode()` 在运行时定义动态关联抓取的语义。

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "Fritz%") )

        .setFetchMode("mate", FetchMode.EAGER)

        .setFetchMode("kittens", FetchMode.EAGER)

        .list();

This query will fetch both `mate` and `kittens` by outer join. See 第 21.1 节 “抓取策略（Fetching strategies）” for more information.

## 17.6. 查询示例

`org.hibernate.criterion.Example` 类允许你通过一个给定实例构建一个条件查询。

    Cat cat = new Cat();

    cat.setSex('F');

    cat.setColor(Color.BLACK);

    List results = session.createCriteria(Cat.class)

        .add( Example.create(cat) )

        .list();

版本属性、标识符和关联被忽略。默认情况下值为 null 的属性将被排除。

你可以自行调整 `Example` 使之更实用。

    Example example = Example.create(cat)

        .excludeZeroes()           //exclude zero valued properties

        .excludeProperty("color")  //exclude the property named "color"

        .ignoreCase()              //perform case insensitive string comparisons

        .enableLike();             //use like for string comparisons

    List results = session.createCriteria(Cat.class)

        .add(example)

        .list();

你甚至可以使用 examples 在关联对象上放置条件。

    List results = session.createCriteria(Cat.class)

        .add( Example.create(cat) )

        .createCriteria("mate")

            .add( Example.create( cat.getMate() ) )

        .list();

## 17.7. 投影（Projections）、聚合（aggregation）和分组（grouping）

`org.hibernate.criterion.Projections` 是 `Projection` 的实例工厂。我们通过调用 `setProjection()` 应用投影到一个查询。

    List results = session.createCriteria(Cat.class)

        .setProjection( Projections.rowCount() )

        .add( Restrictions.eq("color", Color.BLACK) )

        .list();

    List results = session.createCriteria(Cat.class)

        .setProjection( Projections.projectionList()

            .add( Projections.rowCount() )

            .add( Projections.avg("weight") )

            .add( Projections.max("weight") )

            .add( Projections.groupProperty("color") )

        )

        .list();

在一个条件查询中没有必要显式的使用 "group by" 。某些投影类型就是被定义为分组投影，他们也出现在 SQL 的 `group by` 子句中。

你可以选择把一个别名指派给一个投影，这样可以使投影值被约束或排序所引用。下面是两种不同的实现方式：

    List results = session.createCriteria(Cat.class)

        .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )

        .addOrder( Order.asc("colr") )

        .list();

    List results = session.createCriteria(Cat.class)

        .setProjection( Projections.groupProperty("color").as("colr") )

        .addOrder( Order.asc("colr") )

        .list();

`alias()` 和 `as()` 方法简便的将一个投影实例包装到另外一个 别名的 `Projection` 实例中。简而言之，当你添加一个投影到一个投影列表中时你可以为它指定一个别名：

    List results = session.createCriteria(Cat.class)

        .setProjection( Projections.projectionList()

            .add( Projections.rowCount(), "catCountByColor" )

            .add( Projections.avg("weight"), "avgWeight" )

            .add( Projections.max("weight"), "maxWeight" )

            .add( Projections.groupProperty("color"), "color" )

        )

        .addOrder( Order.desc("catCountByColor") )

        .addOrder( Order.desc("avgWeight") )

        .list();

    List results = session.createCriteria(Domestic.class, "cat")

        .createAlias("kittens", "kit")

        .setProjection( Projections.projectionList()

            .add( Projections.property("cat.name"), "catName" )

            .add( Projections.property("kit.name"), "kitName" )

        )

        .addOrder( Order.asc("catName") )

        .addOrder( Order.asc("kitName") )

        .list();

你也可以使用 `Property.forName()` 来表示投影：

    List results = session.createCriteria(Cat.class)

        .setProjection( Property.forName("name") )

        .add( Property.forName("color").eq(Color.BLACK) )

        .list();

    List results = session.createCriteria(Cat.class)

        .setProjection( Projections.projectionList()

            .add( Projections.rowCount().as("catCountByColor") )

            .add( Property.forName("weight").avg().as("avgWeight") )

            .add( Property.forName("weight").max().as("maxWeight") )

            .add( Property.forName("color").group().as("color" )

        )

        .addOrder( Order.desc("catCountByColor") )

        .addOrder( Order.desc("avgWeight") )

        .list();

## 17.8. 离线（detached）查询和子查询

`DetachedCriteria` 类使你在一个 session 范围之外创建一个查询，并且可以使用任意的 `Session` 来执行它。

    DetachedCriteria query = DetachedCriteria.forClass(Cat.class)

        .add( Property.forName("sex").eq('F') );

    Session session = ....;

    Transaction txn = session.beginTransaction();

    List results = query.getExecutableCriteria(session).setMaxResults(100).list();

    txn.commit();

    session.close();

`DetachedCriteria` 也可以用以表示子查询。条件实例包含子查询可以通过 `Subqueries` 或者 `Property` 获得。

    DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)

        .setProjection( Property.forName("weight").avg() );

    session.createCriteria(Cat.class)

        .add( Property.forName("weight").gt(avgWeight) )

        .list();

    DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)

        .setProjection( Property.forName("weight") );

    session.createCriteria(Cat.class)

        .add( Subqueries.geAll("weight", weights) )

        .list();

甚至相互关联的子查询也是有可能的：

    DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")

        .setProjection( Property.forName("weight").avg() )

        .add( Property.forName("cat2.sex").eqProperty("cat.sex") );

    session.createCriteria(Cat.class, "cat")

        .add( Property.forName("weight").gt(avgWeightForSex) )

        .list();

## 17.9. 根据自然标识查询（Queries by natural identifier）

对大多数查询，包括条件查询而言，因为查询缓存的失效（invalidation）发生得太频繁，查询缓存不是非常高效。然而，有一种特别的查询，可以通过不变的自然键优化缓存的失效算法。在某些应用中，这种类型的查询比较常见。条件查询 API 对这种用例提供了特别规约。

首先，你应该对你的 entity 使用 `<natural-id>` 来映射自然键，然后打开第二级缓存。

    <class name="User">

        <cache usage="read-write"/>

        <id name="id">

            <generator class="increment"/>

        </id>

        <natural-id>

            <property name="name"/>

            <property name="org"/>

        </natural-id>

        <property name="password"/>

    </class

    >

注意,此功能对具有mutable自然键的 entity 并不适用。

现在，我们可以用 `Restrictions.naturalId()` 来使用更加高效的缓存算法。

    session.createCriteria(User.class)

        .add( Restrictions.naturalId()

            .set("name", "gavin")

            .set("org", "hb")

        ).setCacheable(true)

        .uniqueResult();

版权 © 2004 Red Hat, Inc.

- 上一页第 16 章 HQL: Hibernate 查询语言
- 上一级
- 起始页
- 下一页第 18 章 Native SQL 查询