377 lines
14 KiB
XML
377 lines
14 KiB
XML
<chapter id="querycriteria">
|
||
<title>
|
||
条件查询(Criteria Queries)
|
||
</title>
|
||
<para>
|
||
具有一个直观的、可扩展的条件查询API是Hibernate的特色。
|
||
</para>
|
||
|
||
<sect1 id="querycriteria-creating">
|
||
<title>创建一个<literal>Criteria</literal> 实例</title>
|
||
|
||
<para>
|
||
<literal>org.hibernate.Criteria</literal>接口表示特定持久类的一个查询。<literal>Session</literal>是
|
||
<literal>Criteria</literal>实例的工厂。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
|
||
crit.setMaxResults(50);
|
||
List cats = crit.list();]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="querycriteria-narrowing">
|
||
<title>限制结果集内容</title>
|
||
|
||
<para>
|
||
一个单独的查询条件是<literal>org.hibernate.criterion.Criterion</literal>
|
||
接口的一个实例。<literal>org.hibernate.criterion.Restrictions</literal>类
|
||
定义了获得某些内置<literal>Criterion</literal>类型的工厂方法。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||
.add( Restrictions.like("name", "Fritz%") )
|
||
.add( Restrictions.between("weight", minWeight, maxWeight) )
|
||
.list();]]></programlisting>
|
||
|
||
<para>
|
||
约束可以按逻辑分组。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||
.add( Restrictions.like("name", "Fritz%") )
|
||
.add( Restrictions.or(
|
||
Restrictions.eq( "age", new Integer(0) ),
|
||
Restrictions.isNull("age")
|
||
) )
|
||
.list();]]></programlisting>
|
||
|
||
<programlisting><![CDATA[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();]]></programlisting>
|
||
|
||
<para>
|
||
Hibernate提供了相当多的内置criterion类型(<literal>Restrictions</literal>
|
||
子类), 但是尤其有用的是可以允许你直接使用SQL。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||
.add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
|
||
.list();]]></programlisting>
|
||
|
||
<para>
|
||
<literal>{alias}</literal>占位符应当被替换为被查询实体的列别名。
|
||
</para>
|
||
|
||
<para>
|
||
|
||
<literal>Property</literal>实例是获得一个条件的另外一种途径。你可以通过调用<literal>Property.forName()</literal>
|
||
创建一个<literal>Property</literal>。
|
||
|
||
</para>
|
||
|
||
<programlisting><![CDATA[
|
||
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();]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="querycriteria-ordering">
|
||
<title>结果集排序</title>
|
||
|
||
<para>
|
||
你可以使用<literal>org.hibernate.criterion.Order</literal>来为查询结果排序。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||
.add( Restrictions.like("name", "F%")
|
||
.addOrder( Order.asc("name") )
|
||
.addOrder( Order.desc("age") )
|
||
.setMaxResults(50)
|
||
.list();]]></programlisting>
|
||
|
||
<programlisting><![CDATA[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();]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="querycriteria-associations">
|
||
<title>关联</title>
|
||
|
||
<para>
|
||
你可以使用<literal>createCriteria()</literal>非常容易的在互相关联的实体间建立
|
||
约束。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||
.add( Restrictions.like("name", "F%")
|
||
.createCriteria("kittens")
|
||
.add( Restrictions.like("name", "F%")
|
||
.list();]]></programlisting>
|
||
|
||
<para>
|
||
注意第二个 <literal>createCriteria()</literal>返回一个新的
|
||
<literal>Criteria</literal>实例,该实例引用<literal>kittens</literal> 集合中的元素。
|
||
</para>
|
||
|
||
<para>
|
||
接下来,替换形态在某些情况下也是很有用的。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||
.createAlias("kittens", "kt")
|
||
.createAlias("mate", "mt")
|
||
.add( Restrictions.eqProperty("kt.name", "mt.name") )
|
||
.list();]]></programlisting>
|
||
|
||
<para>
|
||
|
||
(<literal>createAlias()</literal>并不创建一个新的
|
||
<literal>Criteria</literal>实例。)
|
||
</para>
|
||
|
||
<para>
|
||
<literal>Cat</literal>实例所保存的之前两次查询所返回的kittens集合是
|
||
<emphasis>没有</emphasis>被条件预过滤的。如果你希望只获得符合条件的kittens,
|
||
你必须使用<literal>returnMaps()</literal>。
|
||
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||
.createCriteria("kittens", "kt")
|
||
.add( Restrictions.eq("name", "F%") )
|
||
.returnMaps()
|
||
.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");
|
||
}]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="querycriteria-dynamicfetching" revision="1">
|
||
<title>动态关联抓取</title>
|
||
|
||
<para>
|
||
你可以使用<literal>setFetchMode()</literal>在运行时定义动态关联抓取的语义。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
|
||
.add( Restrictions.like("name", "Fritz%") )
|
||
.setFetchMode("mate", FetchMode.EAGER)
|
||
.setFetchMode("kittens", FetchMode.EAGER)
|
||
.list();]]></programlisting>
|
||
|
||
<para>
|
||
这个查询可以通过外连接抓取<literal>mate</literal>和<literal>kittens</literal>。
|
||
查看<xref linkend="performance-fetching"/>可以获得更多信息。
|
||
</para>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="querycriteria-examples">
|
||
<title>查询示例</title>
|
||
|
||
<para>
|
||
<literal>org.hibernate.criterion.Example</literal>类允许你通过一个给定实例
|
||
构建一个条件查询。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[Cat cat = new Cat();
|
||
cat.setSex('F');
|
||
cat.setColor(Color.BLACK);
|
||
List results = session.createCriteria(Cat.class)
|
||
.add( Example.create(cat) )
|
||
.list();]]></programlisting>
|
||
|
||
<para>
|
||
版本属性、标识符和关联被忽略。默认情况下值为null的属性将被排除。
|
||
</para>
|
||
|
||
<para>
|
||
你可以自行调整<literal>Example</literal>使之更实用。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[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();]]></programlisting>
|
||
|
||
<para>
|
||
你甚至可以使用examples在关联对象上放置条件。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||
.add( Example.create(cat) )
|
||
.createCriteria("mate")
|
||
.add( Example.create( cat.getMate() ) )
|
||
.list();]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="querycriteria-projection">
|
||
<title>投影(Projections)、聚合(aggregation)和分组(grouping)</title>
|
||
<para>
|
||
<literal>org.hibernate.criterion.Projections</literal>是
|
||
<literal>Projection</literal> 的实例工厂。我们通过调用
|
||
<literal>setProjection()</literal>应用投影到一个查询。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||
.setProjection( Projections.rowCount() )
|
||
.add( Restrictions.eq("color", Color.BLACK) )
|
||
.list();]]></programlisting>
|
||
|
||
<programlisting><![CDATA[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();]]></programlisting>
|
||
|
||
<para>
|
||
在一个条件查询中没有必要显式的使用 "group by" 。某些投影类型就是被定义为<emphasis>
|
||
分组投影</emphasis>,他们也出现在SQL的<literal>group by</literal>子句中。
|
||
</para>
|
||
|
||
<para>
|
||
你可以选择把一个别名指派给一个投影,这样可以使投影值被约束或排序所引用。下面是两种不同的实现方式:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||
.setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
|
||
.addOrder( Order.asc("colr") )
|
||
.list();]]></programlisting>
|
||
|
||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||
.setProjection( Projections.groupProperty("color").as("colr") )
|
||
.addOrder( Order.asc("colr") )
|
||
.list();]]></programlisting>
|
||
|
||
<para>
|
||
<literal>alias()</literal>和<literal>as()</literal>方法简便的将一个投影实例包装到另外一个
|
||
别名的<literal>Projection</literal>实例中。简而言之,当你添加一个投影到一个投影列表中时
|
||
你可以为它指定一个别名:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[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();]]></programlisting>
|
||
|
||
<programlisting><![CDATA[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();]]></programlisting>
|
||
|
||
<para>
|
||
你也可以使用<literal>Property.forName()</literal>来表示投影:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
|
||
.setProjection( Property.forName("name") )
|
||
.add( Property.forName("color").eq(Color.BLACK) )
|
||
.list();]]></programlisting>
|
||
|
||
<programlisting><![CDATA[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();]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="querycriteria-detachedqueries">
|
||
<title>离线(detached)查询和子查询</title>
|
||
<para>
|
||
<literal>DetachedCriteria</literal>类使你在一个session范围之外创建一个查询,并且可以使用任意的
|
||
<literal>Session</literal>来执行它。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[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();]]></programlisting>
|
||
|
||
<para>
|
||
<literal>DetachedCriteria</literal>也可以用以表示子查询。条件实例包含子查询可以通过
|
||
<literal>Subqueries</literal>或者<literal>Property</literal>获得。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
|
||
.setProjection( Property.forName("weight").avg() );
|
||
session.createCriteria(Cat.class)
|
||
.add( Property.forName("weight).gt(avgWeight) )
|
||
.list();]]></programlisting>
|
||
|
||
<programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
|
||
.setProjection( Property.forName("weight") );
|
||
session.createCriteria(Cat.class)
|
||
.add( Subqueries.geAll("weight", weights) )
|
||
.list();]]></programlisting>
|
||
|
||
<para>
|
||
甚至相互关联的子查询也是有可能的:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[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();]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
|
||
user objects - similar to setResultClass in JDO2. General use of ResultTransformer
|
||
could also be explained. -->
|
||
</chapter>
|
||
|