DetachedCriteria用法

http://uule.iteye.com/blog/947923转载


在常规的Web编程中,有大量的动态条件查询,即用户在网页上面自由选择某些条件,程序根据用户的选择条件,动态生成SQL语句,进行查询。

  针对这种需求,对于分层应用程序来说,Web层需要传递一个查询的条件列表给业务层对象,业务层对象获得这个条件列表之后,然后依次取出条件,构造查询语句。这里的一个难点是条件列表用什么来构造?传统上使用Map,但是这种方式缺陷很大,Map可以传递的信息非常有限,只能传递name和value,无法传递究竟要做怎样的条件运算,究竟是大于,小于,like,还是其它的什么,业务层对象必须确切掌握每条entry的隐含条件。因此一旦隐含条件改变,业务层对象的查询构造算法必须相应修改,但是这种查询条件的改变是隐式约定的,而不是程序代码约束的,因此非常容易出错。

   DetachedCriteria可以解决这个问题,即在web层,程序员使用DetachedCriteria来构造查询条件,然后将这个 DetachedCriteria作为方法调用参数传递给业务层对象。而业务层对象获得DetachedCriteria之后,可以在session范围内直接构造Criteria,进行查询。就此,查询语句的构造完全被搬离到web层实现,而业务层则只负责完成持久化和查询的封装即可,与查询条件构造完全解耦,非常完美!


   Criteria 和 DetachedCriteria 的主要区别在于创建的形式不一样, 
Criteria 是在线的,所以它是由 Hibernate Session 进行创建的;而 DetachedCriteria 是离线的,创建时无需Session ,DetachedCriteria 提供了 2 个静态方法 forClass(Class) 或 forEntityName(Name)进行DetachedCriteria 实例的创建。
   Spring 的框架提供了getHibernateTemplate().findByCriteria(detachedCriteria) 方法可以很方便地根据DetachedCriteria 来返回查询结果。
 
Criteria:
查詢User表格中的所有資料:
Criteria criteria = session.createCriteria(User.class);
// 查詢user所有欄位
List users = criteria.list();
Iterator iterator =  users.iterator();
while(iterator.hasNext()) {
    User user = (User) iterator.next();
    System.out.println(user.getId() + user.getName());           
}

Hibernate实际上使用以下的SQL來查詢:
select this_.id as id0_, this_.name as name0_0_ from user this_

Criteria只是个容器,如果想要設定查詢條件,則要使用add()方法加入Restrictions的条件限制 ,例如查詢age大於20且小於40的資料:
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.gt("age", new Integer(20)));
criteria.add(Restrictions.lt("age", new Integer(40)));
List users = criteria.list();

您也可以使用逻辑組合來进行查詢,例如結合age等於(eq)20或(or)age為空(isNull)的條件:
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.or(
                   Restrictions.eq("age", new Integer(20)),
                   Restrictions.isNull("age")
               ));
List users = criteria.list();



DetachedCriteria的关联查询:
假设要通过stuName查询一个学生Student记录,可以如下:  
DetachedCriteria dc = DetachedCriteria.forClass(Student.class);
    dc.add(Restrictions.like("stuName", stuName, MatchMode.ANYWHERE)); 
      
如果要通过Student的Team的teamName查询一个Student记录,很多人都会这么写: 
DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
    dc.add(Restrictions.like("team.teamName", teamName, MatchMode.ANYWHERE)); 
      
遗憾的是上述程序报错,说是在Student中找不到team.teamName属性,这是可以理解的。那么如何通过teamName查找Student呢? 
可以这么写: 
DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
    dc.createAlias("team", "t"); 
    dc.add(Restrictions.like("t.teamName", teamName, MatchMode.ANYWHERE)); 
      没错,就是要先建立team的引用,才能用team导航到teamName 。

这里有一个特殊情况,如果是对引用对象的id查询,则可以不用建立引用,也就是可以不调用createAlias()语句,如下所示: 
DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
    dc.add(Restrictions.like("team.id", teamId, MatchMode.ANYWHERE)); 
      据我个人的经验,team后只能跟其主键属性,比较其他属性要用别名。此主键属性可以用“id”字符来指代,也可以用team的主键属性来指代。换句话说,我的Student类的类主键“stuId”,不管是在HQL还是在QBC中,都可以用stu.id来指代stu.stuId。在这里可以看出 “id”字符的特殊性。上述是个人观点,并未得到确实的证实。


DetachedCriteria:
例子1:如果每个美女都有自己的客户资源(不要想歪了!),那么需要查询拥有客户Gates的美女怎么办?
两种方法:
1:
DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class).createCriteria("customers");
beautyCriteria.add(Restrictions.eq("name", "Gates")):

2:
DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class).createAlias("customers", "c");
beautyCriteria.add(Restrictions.eq("c.name", "Gates")):

接着有了新的要求,年纪太大的美女不要,还是查找拥有客户Gates的,条件如下:
DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class, "b").;
DetachedCriteria customerCriteria = beautyCriteria.createAlias("customers", c");
beautyCriteria.add(Restrictions.le("b.age", new Long(20))):
customerCriteria.add(Restrictions.eq("c.name", "Gates")):

关于Criteria更详细的资料,Hibernate的源代码和测试是最好的文档。
Criteria的缺点?DBA很生气,后果很严重。


例子2: 
Department和Employee是一对多关联,查询条件为: 
  名称是“department”开发部门; 
  部门里面的雇员年龄大于20岁;


DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Department.class);
detachedCriteria.add(Restrictions.eq("name", "department"))
                 .createAlias("employees", "e")
                 .add(Restrictions.gt(("e.age"), new Integer(20)));


List  list = this.getHibernateTemplate().findByCriteria(detachedCriteria);
 


Java代码 
 


detachedCriteria.add(Expression.like("citycode", markuplayer8.getCitycode(),MatchMode.ANYWHERE)); 


[java] 
view plaincopyprint?


detachedCriteria.add(Expression.like("citycode", markuplayer8.getCitycode(),MatchMode.ANYWHERE)); 

  SQL:


Java代码 
 


if(startdate!=null && !startdate.equals("") && enddate!=null && !enddate.equals("")){
               StringBuffer sb = new StringBuffer();               sb.append("enddate >='" + enddate + "' and '"+startdate+"' <= startdate");               detachedCriteria.add(Expression.sql(sb.toString()));           }           List<Markuplayer8> markuplayerList = this.getHibernateTemplate().findByCriteria(detachedCriteria); 


[java] 
view plaincopyprint?


if(startdate!=null && !startdate.equals("") && enddate!=null && !enddate.equals("")){              StringBuffer sb = new StringBuffer();              sb.append("enddate >='" + enddate + "' and '"+startdate+"' <= startdate");              detachedCriteria.add(Expression.sql(sb.toString()));          }          List<Markuplayer8> markuplayerList = this.getHibernateTemplate().findByCriteria(detachedCriteria); 

 


重要:


http://dev.yesky.com/241/2033241.shtml
http://www.tup.com.cn/Resource/tsyz/027541-01.txt
http://www.blogjava.net/hilor/archive/2007/09/14/145172.html
 
很全的例子:
http://blog.csdn.net/kjfcpua/archive/2009/06/21/4287248.aspx
1. 创建一个Criteria 实例 
org.hibernate.Criteria接口表示特定持久类的一个查询。Session是 Criteria实例的工厂。
Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50); 
List cats = crit.list(); 
   
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.sql("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(); 
   
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(); 
   
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, 你必须使用returnMaps()。

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

5. 动态关联抓取
你可以使用setFetchMode()在运行时定义动态关联抓取的语义。 
   
List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.like("name", "Fritz%") )
    .setFetchMode("mate", FetchMode.EAGER)
    .setFetchMode("kittens", FetchMode.EAGER)
    .list(); 
   
这个查询可以通过外连接抓取mate和kittens。 
   
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(); 
  
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(); 
  
8. 离线(detached)查询和子查询 
DetachedCriteria类使你在一个session范围之外创建一个查询,并且可以使用任意的 Session来
执行它。 
   
DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
    .add( Property.forName("sex").eq('F') );
//创建一个Session 
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();

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值