Hibernate.orgCommunity Documentation

第17章 Criteria クエリ

17.1. Criteria インスタンスの作成
17.2. リザルトセットの絞込み
17.3. 結果の整列
17.4. 関連
17.5. 関連の動的フェッチ
17.6. クエリの例
17.7. 射影、集約、グループ化
17.8. クエリおよびサブクエリの分離
17.9. 自然識別子によるクエリ

Hibernate には、直感的で拡張可能な criteria クエリ API が用意されています。

org.hibernate.Criteria インターフェースは特定の永続性クラスに対するクエリを表現します。 SessionCriteria インスタンスのファクトリです。

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

crit.setMaxResults(50);
List cats = crit.list();

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

Restriction (限定)は、論理的にグループ化できます。

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

元々ある Criterion 型(Restrictions のサブクラス) はかなりの範囲に及びますが、特に有用なのは SQL を直接指定できるものです。

List cats = sess.createCriteria(Cat.class)

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

{alias} というプレースホルダは、問い合わせを受けたエンティティの行の別名によって置き換えられます。

criterion を得る別の手段は、 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();

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

By navigating associations using createCriteria() you can specify constraints upon related entities:

List cats = sess.createCriteria(Cat.class)

    .add( Restrictions.like("name", "F%") )
    .createCriteria("kittens")
        .add( Restrictions.like("name", "F%") )
    .list();

2番目の createCriteria() は、 kittens コレクションの要素を参照する新しい Criteria インスタンスを返すことに注意してください。

以下のような方法も、状況により有用です。

List cats = sess.createCriteria(Cat.class)

    .createAlias("kittens", "kt")
    .createAlias("mate", "mt")
    .add( Restrictions.eqProperty("kt.name", "mt.name") )
    .list();

createAlias() は新しい Criteria インスタンスを作成しません。)

前の2つのクエリによって返される Cat インスタンスによって保持される kittens コレクションは、 criteria によって事前にフィルタリング されない ことに注意してください。もし criteria に適合する kitten を取得したいなら、 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");
}

Additionally you may manipulate the result set using a 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();
        
        

This will return all of the Cats with a mate whose name starts with "good" ordered by their mate's age, and all cats who do not have a mate. This is useful when there is a need to order or limit in the database prior to returning complex/large result sets, and removes many instances where multiple queries would have to be performed and the results unioned by java in memory.

Without this feature, first all of the cats without a mate would need to be loaded in one query.

A second query would need to retreive the cats with mates who's name started with "good" sorted by the mates age.

Thirdly, in memory; the lists would need to be joined manually.

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 「フェッチ戦略」 for more information.

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

関連オブジェクトに criteria を指定するために、 example を使うことも可能です。

List results = session.createCriteria(Cat.class)

    .add( Example.create(cat) )
    .createCriteria("mate")
        .add( Example.create( cat.getMate() ) )
    .list();

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

必要であっても、 criteria クエリに「group by」を明示する必要はありません。ある種の Projection 型は グループ化射影 として定義され、 SQL の group by 節にも現れます。

An alias can be assigned to a projection so that the projected value can be referred to in restrictions or orderings. Here are two different ways to do this:

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 インスタンスを別の名前の 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();

DetachedCriteria クラスにより、セッションスコープ外にクエリを作成できます。後で、任意の 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 は、サブクエリを表現するためにも使えます。サブクエリを伴う Criterion インスタンスは、 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();

criteria クエリを含むたいていのクエリにとって、クエリキャッシュはあまり効率がよくないです。なぜなら、クエリキャッシュが頻繁に無効になるためです。しかしながら、キャッシュを無効にするアルゴリズムを最適化できる特別なクエリの種類が1つあります。更新されない自然キーによる検索です。いくつかのアプリケーションでは、この種類のクエリが頻繁に現れます。このような使われ方のために、 criteria API は特別な対策を提供します。

最初に、 <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
>

注記: 変更される 自然キーを持つエンティティにこの機能を使うのは、意図されていない使い方です。

これで、 Restrictions.naturalId() により、より効率的なキャッシュアルゴリズムを使用できます。

session.createCriteria(User.class)

    .add( Restrictions.naturalId()
        .set("name", "gavin")
        .set("org", "hb") 
    ).setCacheable(true)
    .uniqueResult();