Query By Criteria

来源:互联网 发布:mv播放器软件 编辑:程序博客网 时间:2024/05/01 21:30
Hibernate QBC查询 
            QBC查询: 
            QBC查询就是通过使用Hibernate提供的Query By Criteria 
            API来查询对象,这种API封装了SQL语句的动态拼装,对查询提供了更加面向对象的功能接口。我们看下面的示例程序: 
            Criteria criteria=session.createCriteria(User.class); 
            criteria.add(Expression.eq(“name”,"zx”)); 
            criteria.add(Expression.eq(“age”,new Integer(27)); 
            List list=criteria.list(); 
            当执行criteria.list()时会生成类似这样的SQL语句:Select * from user where name=’zx’  and age=27;


    所以在这里我们可以看出,Criteria实际上是一个查询容器,它对查询条件表达式的添加进行了封装,具体的查询条件是通过add()方法添加的,而且具体的查询条件的表达式运算是通过Expression指定的。Hibernate在运行期会根据Criteria指定的表达式条件来添加查询条件,并且生成查询语句。这种方式非常符合Java以及所有面向对象编程语言的编程方式,所以大多数的持久层框架都提供了对这种方式查询的支持。


    下面我们讲解这种查询方式的各个技术细节。 
            1、Criteria查询表达式: 
            正如我们所见,Expression对查询语句的表达式进行了封装和限制,下表列出了Expression所有的方法,以及每个方法所对应的查询表达式及其限制。 
                  方法描述 
                  Expression.eq对应SQL的"field=value”表达式 
                  如:Expression.eq(“name”,"zx”); 
                  Expression.allEq方法的参数为一个Map类型对象,包含多个名/值对对应关系,相当于多个Expression.eq的叠加 
                  Expression.gt对应SQL的"field>value”表达式 
                  Expression.ge对应SQL的"field>=value”表达式 
                  Expression.lt对应SQL的"field<value”表达式 
                  Expression.le对应SQL的“field<=value”表达式 
                  Expression.between对应SQL语句的between表达式,如:查询年龄在21与27岁之间的用户,可以写成Expression.between(“age”,new Integer(21),new Integer(27)); 
                  Expression.like对应SQL语句的"field like value”表达式 
                  Expression.in对应SQL语句的"field in(……)"表达式 
                  Expression.eqProperty用于比较两个属性值,对应"field=field”SQL表达式 
                  Expression.gtProperty用于比较两个属性值,对应"field>field”SQL表达式 
                  Expression.geProperty用于比较两个属性值,对应"field>=field”SQL表达式 
                  Expression.ltProperty用于比较两个属性值,对应"field表达式 
                  Expression.leProperty用于比较两个属性值,对应"field<=field”SQL表达式 
                  Expression.and对应SQL语句的And关系组合,如:Expression.and(Expression.eq(“name”,"zx”),Expression.eq(“sex”,”1”)); 
                  Expression.or对应SQL语句的Or关系组合,如:Expression.or(Expression.eq(“name”,"zx”),Expression.eq(“name”,"zhaoxin”)); 
                  Expression.sql作为补充这个方法提供了原生SQL语句查询的支持,在执行时直接通过原生SQL语句进行限定,如:Expression.sql(“lower({alias}.name) 
                  like (?)”,"zhao%”,Hibernate.STRING) ;在运行时{ alias }将会由当前查询所关联的实体类名替换,()中的?将会由”zhao%”替换,并且类型由Hibernate.STRING指定。 
            注意:Expression各方法中的属性参数(各方法中的第一个参数)所指定的属性名称(如:name,sex),并不是数据库表中的实际字段名称,而是实体对象中映射实际数据表字段的类属性名称。 
            2、示例查询: 
            示例查询是通过Example类来完成的,Example类实现了Criterion接口,可以用作Criteria查询条件,Example类的作用是:根据已有对象,查询属性值与之相同的其他对象。如下代码所示: 
            Criteria criteria=session.createCriteria(User.class); 
            User exampleuser=new User(“zx”); 
            criteria.add(Example.create(exampleuser)); 
            List list=criteria.list(); 
            for(int i=0;i 
               User user=(User)list.get(i); 
               System.out.println(user.getName()+”\n”); 
            } 
            上述代码中User exampleuser=new 
            User(“zx”);criteria.add(Example.create(exampleuser));两句相当于 
            criteria.add(Expression.eq(“name”,"zx”));因此会生成类似如下的SQL语句: 
            select * from user where name=’zx’;在上面的代码中exampleuser称为示例对象。 
            在Hibernate中队示例查询,默认情况下会排除掉示例对象中属性值为空的属性,还可以调用Example.excludeNone(排除空串值)/excludeZeros(排除零值),或者调用Example.excludeProperty方法来指定排除特定属性。 
            示例查询主要应用于组合查询中,比如根据用户输入的查询条件动态生成最终的查询语句,通过使用示例查询,可以避免由于查询条件过多而写的大量if判断语句。 
            3、复合查询: 
            复合查询主要是处理,具有关联关系的两个实体怎样进行关联查询,比如User实体对象与Addres实体对象具有一对多的关联关系,我们可以如下构造符合查询: 
            Criteria criteria=session.createCriteria(User.class); 
               Criteria addcriteria=criteria.createCriteria(“addresses”);(1) 
               addcriteria.add(Express.like(“address”,”%tianjin%”)); 
              List list=criteria.list(); 
               for(int i=0;i 
                 User user=(User)list.get(i); 
                 System.out.println(user.getName()+”\n”); 
                 Set addresses=user.getAddresses(); 
                 Iterator it=addresses.iterator(); 
                 while(it.hasNext(){ 
                  Address address=(Address)it.next(); 
                  System.out.println(address.getAddress()+”\n”); 
                 } 
               } 
            当执行到了(1)处时,表明要针对User对象的addresses属性添加新的查询条件,因此当执行criteria.list()时,Hibernate会生成类似如下的SQL语句: 
            Select * from user inner join address on user.id=address.id where 
            address.address like ‘%shanghai%’; 
            正如我们所见,我们可以通过向Criteria中添加保存关联对象的集合属性(addresses属性保存与User对象相关联的Address对象),来构造复合查询,在数据库一端是通过内连接查询来实现。 
            4、Criteria的高级特性: 
            A、限定返回记录条数: 
             我们可以通过利用Criteria.setFirstResult/setMaxResult方法来限定返回某一次查询的记录数,如下代码: 
            Criteria criteria=session.createCriteria(User.class); 
            criteria.setFirstResult(100); 
            criteria.setMaxResult(200); 
            通过以上代码可以设定该次查询返回user表中的从第100条记录开始直到第200条记录结束的100条记录。 
            B、对查询结果进行排序: 
             可通过使用net.sf.hibernate.expression.Order类可以对查询结果集进行排序,如下面代码: 
            Criteria criteria=session.createCriteria(User.class); 
            criteria.add(Expression.eq(“groupid”,”2”); 
            criteria.addOrder(Order.asc(“name”)); 
            criteria.addOrder(Order.desc(“groupid”)); 
            List list=criteria.list(); 
            通过使用Order类的asc()/desc()方法,可以指定针对某个字段的排序逻辑,如果执行上述代码,会生成类似如下的SQL语句: 
            Select * from user where groupid=’2’ order by name asc,groupid desc 
            C、分组与统计: 
            在Hibernate3中,对Criteria又增添了新功能,可以支持分组与统计功能,在Hibernate3中增加了Projections以及ProjectionList类,这两个类对分组与统计功能进行了封装,如下代码: 
            Criteria criteria=session.createCriteria(User.class); 
            criteria.setProjection(Projections.groupProperty(“age”));(1) 
            List list=criteria.list(); 
            Iterator it=list.iterator(); 
            while(it.hasNext()){ 
             System.out.println(it.next()); 
            } 
            通过(1)处的代码,我们通过Projections类指定了用于分组的目标属性,当进行检索时Hibernate会生成类似如下的SQL语句: 
            Select age from user group by age; 
            还可以通过使用Projections的avg()/rowCount()/count()/max()/min()/countDistinct()等方法来实现统计功能,如下面的代码示例: 
            Criteria criteria=session.createCriteria(User.class); 
            criteria.setProjection(Projections.avg(“age”));(1) 
            List list=criteria.list(); 
            Iterator it=list.iterator();   // .iterator() 有缓存 但如果session里没有的话 会比list还慢 
            while(it.hasNext()){ 
             System.out.println(it.next()); 
            } 
            通过(1)处的代码,我们实现了对用户平均年龄的统计,当进行检索时,Hibernate会生成类似如下的SQL语句: 
            Select avg(age) from user; 
            另外,在SQL语句中的多条件分组与统计功能,可以利用ProjectionList类来实现,如下面代码所示: 
            Criteria criteria=session.createCriteria(User.class); 
            ProjectionList prolist=Projections.projectionList(); 
            prolist.add(Projections.groupProperty(“age”)); 
            prolist.add(Projections.rowCount()); 
            criteria.setProjection(prolist); 
            List list=criteria.list(); 
            通过以上代码,实现了对不同年龄人员数量的分组统计,当进行检索时,Hibernate会生成类似如下的SQL语句: 
            Select age,count(*) from user group by age; 
            5、DetachedCriteria: 
            在Hibernate2中,Criteria实例是与创建它的Session实例具有相同的生命周期的,也就是说,Session实例是它所创建的Criteria实例的宿主,当Session关闭时,寄生于Session实例的Criteria都将失效。这就对Criteria的重用造成了困难,为了实现Criteria实例的重用,在Hibernate3中提供了一个DetachedCriteria类,DetachedCriteria实例的生命周期与Session实例的生命周期无关,我们可以利用DetachedCriteria对一些常用的Criteria查询条件进行抽离,当需要进行检索时再与Session实例关联,从而获得运行期的Criteria实例。如下面的代码所示: 
               DetachedCriteria dc= DetachedCriteria.forClass(User.class); 
               dc.add(Expression.eq(“name”,"zhaoxin”)); 
               dc.add(Expression.eq(“sex”,”1”)); 
               Criteria criteria=dc.getExecutableCriteria(session); 
               Iterator it=criteria.list().iterator(); 
               while(it.hasNext()){ 
                 User user=(User)it.next(); 
                 System.out.println(user.getName()); 
               } 
            正如我们所见,DetachedCriteria的生存周期与session实例无关,当需要进行检索时,通过getExecutableCriteria(session)方法,与当前的Session实例关联并获得运行期的Criteria实例,完成检索。 
            DetachedCriteria也可以用于完成子查询功能,如下代码所示: 
            DetachedCriteria dc= DetachedCriteria.forClass(User.class); 
            dc.setProjection(Projections.avg(“age”)); 
            Criteria criteria=session.createCriteria(User.class); 
            criteria.add(Subqueries.propertyGt(“age”,dc)); 
            List list=criteria.list();   // .list();无缓存 
            通过Subqueries类,实现了添加子查询的功能,我们将DetachedCriteria所设定的查询条件,当作子查询添加到了运行时Criteria实例的查询条件中,当执行检索时Hibernate会生成类似如下的SQL语句: 
            Select * from user where age>(select avg(age) from user group by 
            age); 
Restrictions类的常用方法 
Restrictions.eq(String propertyName,Object value) 
等于 
Restrictions.allEq(Map propertyNameValues) 
使用Map key/value进行多个等于的比对 
Restrictions.gt(String propertyName, Object value) 
大于 >    (gt----->greater than) 
Restrictions.ge(String propertyName, Object value) 
大于等于 >=    (ge----->greater equal) 
Restrictions.It(String propertyName, Object value) 
小于< (It---->less than) 
Restrictions.Le(String propertyName, Object value) 
小于等于<= (le---->less equal) 
Restrictions.between(String propertyName, Object lo, Object hi) 
对应SQL语句的Between子句 
Restrictions.like(String propertyName, Object value) 
对应SQL语句的LIKE子句 
Restrictions.in(String propertyName, Collection value) 
对应SQL语句的in子句 
Restrictions.and(Criterion lhs, Criterion rhs) 
And关系 
Restrictions.or(Criterion lhs, Criterion rhs) 
Or关系 
Restrictions.sqlRestriction(String sql,Object[] values,Type[] types) 
SQL限定查询 
          工具类Order提供设置排序方式 
Order.asc(String propertyName) 
升序排序 
Order.desc(String propertyName) 
降序排序 
         工具类Projections提供对查询结果进行统计与分组操作 
Porjections.avg(String propertyName) 
求某属性的平均值 
Projections.count(String propertyName) 
统计某属性的数量 
Projections.countDistinct(String propertyName) 
统计某属性的不同值的数量 
Projections.groupProperty(String propertyName) 
指定一组属性值 
Projections.max(String propertyName) 
某属性的最大值 
Projections.min(String propertyName) 
某属性的最小值 
Projections.projectionList() 
创建一个新的projectionList对象 
Projections.rowCount() 
查询结果集中记录的条数 
Projections.sum(String propertyName) 
返回某属性值的合计 
List cats = session.createCriteria(Cat.class)   
    .createAlias("kittens", "kt")   
    .createAlias("mate", "mt")   
    .add( Restrictions.eqProperty("kt.name", "mt.name") )   
    .list();  
这样对于关联的表可以实现,但是如果是非关联的表如何实现. 
也就是 
select * from kittens kt, mate mt where kt.name=mt.name