Criteria Query的使用
Criteria 查询表达式
Criteria 本身只是一个查询容器,具体的查询条件需要通过Criteria.add方法加到Criteria实例中。
示例1:
Criteria criteria = session.createCriteria(TPerson.class); criteria.add(Restrictions.eq("name", "oham")); criteria.add(Restrictions.eq("address", "earth")); criteria.list();
执行sql:
select this_.id as id0_0_, this_.name as name0_0_, this_.address as address0_0_, this_.tel as tel0_0_, this_.zipcode as zipcode0_0_ from t_person this_ where this_.name=? and this_.address=?
注意, 1)鄙人使用的Hibernate版本为4.1.4.Final,示例1的中Restrictions是先前版本的Expression的替代,在4.1.4.Final中,Expression已被标记为过期。
2)针对SQL语法,Restrictions提供了对应的查询机制,可以去看看:http://docs.jboss.org/hibernate/orm/4.1/javadocs/
3)Restriction中的各个方法的属性名参数是指POJO中的属性名,非真实数据库表的字段名。
Example类的使用
Example实现了Criteria接口,所以也可以用作Criteria的查询条件。Example的作用是,根据已有的对象,查找属性与之相符的其他对象。一般用于组合查询,如,界面上提供若干查询选项,然后根据用户所选返回符合条件的结果。
示例2:
Criteria criteria = session.createCriteria(TPerson.class); Example example; example = Example.create(examplePerson); /** * 默认的情况下,Hibernate会过滤掉示例对象的Null值属性,可以通过调用 * excludeNone/excludeZeroes方法进行调整,或则通过excludeProperty将 * 某个属性排除在外。 * 1.excludeNone —— 不过滤Null值的属性 * 2.excludeZeroes —— 过滤值为0 的属性 * 3.请测测example.excludeNone与example.excludeZeroes,若两者调用,貌似 * 只用后者起作用。。。 */ // example.excludeZeroes(); // example.excludeNone(); // example.excludeProperty("address"); criteria.add(example); criteria.list();
执行上述的代码的sql:
select this_.id as id0_0_, this_.name as name0_0_, this_.address as address0_0_, this_.tel as tel0_0_, this_.zipcode as zipcode0_0_, this_.age as age0_0_ from t_person this_ where ( this_.name=? and this_.address=? and this_.age=? )
Criteria复合查询
现在引入一个一对多的场景:
执行下面的代码,打印出所有的person以及对应的skill:
Criteria criteria = session.createCriteria(TPerson.class); ListpersonList = criteria.list(); for(TPerson person : personList) { System.out.println(person.getName()); Set set = person.getSkills(); for(TSkill skill : set) { System.out.println("\t\t" + skill.getName()); } }
结果:
Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.address as address0_0_, this_.tel as tel0_0_, this_.zipcode as zipcode0_0_, this_.age as age0_0_ from t_person this_ Hibernate: select skills0_.pid as pid0_1_, skills0_.id as id1_, skills0_.id as id1_0_, skills0_.name as name1_0_, skills0_.pid as pid1_0_ from t_skill skills0_ where skills0_.pid=? Oham meditation kill
如果现在想查出所有具有kill技能的person,可以使用复合查询:
Criteria criteria = session.createCriteria(TPerson.class); //在原有的查询基础上,针对TPerson对象的skills属性构造新的查询过滤条件 Criteria addCriteria = criteria.createCriteria("skills"); addCriteria.add(Restrictions.like("name", "%kill%")); ListpersonList = criteria.list(); for(TPerson person : personList) { System.out.println(person.getName()); Set set = person.getSkills(); for(TSkill skill : set) { System.out.println("\t\t" + skill.getName()); } }
Hibernate会在运行期构造以下的sql:
select this_.id as id0_1_, this_.name as name0_1_, this_.address as address0_1_, this_.tel as tel0_1_, this_.zipcode as zipcode0_1_, this_.age as age0_1_, tskill1_.id as id1_0_, tskill1_.name as name1_0_, tskill1_.pid as pid1_0_ from t_person this_ inner join t_skill tskill1_ on this_.id=tskill1_.pid where tskill1_.name like ?
DetachedCriteria
criteria对象生于session,所以其生命周期位于session之内,即虽不同生,必同死。。。这样就很大程度限制了Criteria的重用,对于相同的查询条件,在不同的session实例里需要再实例一个Criteria。
DetachedCriteria是可以脱离Session实例而独立存在的,于是我们可以使用其将某些通用的Criteria查询条件进行抽离,然后使用的时候将其再与当前的session实例绑定。
示例3:
//生成detachedCriteria的实例 DetachedCriteria detachedCriteria = DetachedCriteria.forClass(TPerson.class); //detachedCriteria的用法与criteria基本类似 DetachedCriteria addDetachedCriteria = detachedCriteria.createCriteria("skills"); addDetachedCriteria.add(Restrictions.like("name", "%kill%")); SessionFactory factory = HibernateLocalUtil.getSessionFactory(); Session session = factory.openSession(); //调用getExecutableCriteria与当前的session实例绑定 Criteria criteria = addDetachedCriteria.getExecutableCriteria(session); ListpersonList = criteria.list(); for(TPerson person : personList) { System.out.println(person.getName()); Set set = person.getSkills(); for(TSkill skill : set) { System.out.println("\t\t" + skill.getName()); } } session.close();
DetachedCriteria也可用于子查询,现在想查询大于平均年龄的所有person。
示例4:
DetachedCriteria avgAge = DetachedCriteria.forClass(TPerson.class); avgAge.setProjection(Projections.avg("age")); Criteria criteria = session.createCriteria(TPerson.class); criteria.add(Subqueries.propertyGt("age", avgAge)); Listlist = criteria.list();
生成执行 sql:
select this_.id as id0_0_, this_.name as name0_0_, this_.address as address0_0_, this_.tel as tel0_0_, this_.zipcode as zipcode0_0_, this_.age as age0_0_ from t_person this_ where this_.age > ( select avg(this_.age) as y0_ from t_person this_ )
Criteria的其他特性辑录
——限定返回结果范围:
Criteria criteria = session.createCriteria(TPerson.class); //从100条结果开始的20条记录 criteria.setFirstResult(100); criteria.setMaxResults(20);
——排序:
//按名字(顺序)和地址(逆序)排序 criteria.addOrder(Order.asc("name")); criteria.addOrder(Order.desc("address"));
——分组与统计:
//对年龄age进行分组 criteria.setProjection(Projections.groupProperty("age"));
生成sql:
select this_.age as y0_ from t_person this_ group by this_.age
对于多条件组合的分组、统计功能,可以借助ProjectionList,现在统计各个年龄的person数量,
示例5:
ProjectionList pList = Projections.projectionList(); pList.add(Projections.groupProperty("age")); pList.add(Projections.rowCount()); criteria.setProjection(pList); criteria.list();
生成sql:
select this_.age as y0_, count(*) as y1_ from t_person this_ group by this_.age
HQL(Hibernate Query Language)
Hibernate提供的一套类SQL的查询语言。
具体语法: [select / update / delete ...] [from ...] [where ... ] [group by ...] [having ...] [order by ...],用起来与SQL十分类似,但注意好像HQL没有insert之用。
注意“
1.若查询或操作一个实体表的数据,from 后面跟的是实体类名,可以采用全路径类名,特别是同名不同包的情况。
2.HQL字句本身大小写无关,但其中出现的类名和属性名需要区分大小写。
3.若查询的目标实体存在继承关系的判定,例如 TUser 有两个子类TSystemUser和TAdmin,分别做了实体映射, 那么 from TUser 这条HQL返回的记录将包含这两个子类的所有数据,而包含的字段是与父类公有的那些。
4.from java.lang.Object 这句将返回数据库所有表的数据。
以下示例基于如下表关系:
用法举例:
1.获取某个实体的几个属性时可以这样:select u.name, u.age from TUser as u,而此时,list结果集中每个条目是个Object[]类型,不是实体类型,依次包含了所获取的各个属性数据。可以让其返回实体类对象,这样就行了:select new TUser(u.id, u.name) from TUser u,前提是TUser必须有这个构造函数(不好用。。。)。
下面是测试代码:
String hql1 = "from TUser"; String hql2 = "select u.id, u.name from TUser u"; String hql3 = "select new TUser(u.id, u.name) from TUser u"; // List list = session.createQuery(hql1).list(); // List list = session.createQuery(hql2).list(); List list = session.createQuery(hql3).list(); Iterator iterator = list.iterator(); while(iterator.hasNext()) { //for hql and hql3 TUser u = (TUser)iterator.next(); System.out.println(u.getName()); // for hql2 /*Object[] results = (Object[])iterator.next(); System.out.println(results[1]);*/ }
2.动态的参数绑定:
(1)使用query接口方法:
String hql = "from TUser u where u.name = ?"; Query query = session.createQuery(hql); query.setString(0, "lulu"); List list = query.list();
(2)使用占位符:
String hql = "from TUser u where u.name = :name"; Query query = session.createQuery(hql); query.setParameter("name", "Lulu");
说明动态的参数绑定机制可以使得查询语法与具体的参数值相互独立,这样,对于参数不同,SQL语法相同的查询操作,数据库可以实施性能优化策略。同时避免了参数值对语法本身的影响,也就避免了SQL Injection。
3.HQL的引用查询,将HQL语句本身从代码中抽出,写在映射配置文件当中。
然后代码中:
Query query = session.getNamedQuery("TUser.getByName"); query.setParameter("name", "Lulu");
4.HQL的联合查询,就是跟SQL的inner join ,left outter, right outer join, full join 差不多的。
注意的是:
1. 关键字fetch ,看示例代码:
String hql = "from TUser u join fetch u.addrs"; Query query = session.createQuery(hql); List list = query.list(); Iterator it = list.iterator(); while(it.hasNext()) { TUser u = (TUser)it.next(); System.out.print(u.getName() + "----"); System.out.println(u.getAddrs().size()); }
上述的HQL对应SQL为:
select ... from t_user U inner join t_address addr on uid=addr.uid
(其中因为on uid=addr.uid所描述的对应关系已在映射文件中指定,所以HQL无对应的表现)
这里的fetch表示查出address对象后立刻立刻填充到对应的TUser对象的address集合属性中。若不加fetch,则list当中每个条目是个Object[],其中Object[]包含了一个TUser对象和对应的一个TAddress对象,运行代码:
String hql = "from TUser u join u.addrs"; Query query = session.createQuery(hql); List list = query.list(); Iterator it = list.iterator(); while(it.hasNext()) { Object[] arr = (Object[])it.next(); for(int i=0; i< arr.length; i++) { if(arr[i] instanceof TUser) { System.out.println( ((TUser)arr[i]).getName() ); }else if(arr[i] instanceof TAddress) { System.out.println( "---" + ((TAddress)arr[i]).getAddr() ); } } }
输出结果:
Hibernate: select tuser0_.id as id0_0_, addrs1_.id as id1_1_, tuser0_.name as name0_0_, addrs1_.uid as uid1_1_, addrs1_.addr as addr1_1_ from t_user tuser0_ inner join t_address addrs1_ on tuser0_.id=addrs1_.uid Cancan ---Mars Oham ---Earth Lulu ---Moon Lulu ---Earth Maomao ---Venus
2.fetch 只对inner join 和 left outer join 有效,这是因为想right outer join,full join之类,作为关联对象可能为空,因此Hibernate无法通过fecth 进行集合填充操作。
数据加载方式
Hibernate中支持以下的数据加载方式:
1.即时加载(Immediate Loading)—— 当实体加载完毕,立即加载其关联的数据。
2.延迟加载(Lazy Loading)—— 实体加载时,其关联数据不是立刻获取,而是当关联数据第一次被访问时再进行读取。
3.预先加载(Eager Loading)—— 实体及其关联对象同时读取,这与即时加载类似,不过实体及关联数据是通过一条SQL语句(基于外连接[outer join])同时读取。
4.批量加载(Batch Loading)—— 对于即时加载和延迟加载,可以采用批量加载方式进行性能上的优化。
举例说明,这里引入TUser及其地址的一对多关联。
即时加载,TUser映射中描述关联关系的配置段:
注意lazy=“false”。运行代码:
Criteria criteria = session.createCriteria(TUser.class); criteria.add(Restrictions.eq("name", "lulu")); List list = criteria.list();输出结果:
Hibernate: select this_.id as id0_0_, this_.name as name0_0_ from t_user this_ where this_.name=? Hibernate: select addrs0_.uid as uid0_1_, addrs0_.id as id1_, addrs0_.id as id1_0_, addrs0_.uid as uid1_0_, addrs0_.addr as addr1_0_ from t_address addrs0_ where addrs0_.uid=?可以看出即时加载的时候,当加载关联主题TUser的时候,Hibernate会自动读取其关联的数据并完成关联属性的填充。
延迟加载,有时我们只想取得TUser的信息,无需取得其对应的地址信息,即使用延迟加载,相应的关联关系配置段:
把lazy设置为“true”就行了。执行代码:
Criteria criteria = session.createCriteria(TUser.class); criteria.add(Restrictions.eq("name", "lulu")); List list = criteria.list();输出结果:
Hibernate: select this_.id as id0_0_, this_.name as name0_0_ from t_user this_ where this_.name=?可以看出此时只加载了TUser的数据,没有相应的Address信息,若加上读取Address的代码: