Hibernate.Criteria查询
1.序言
Hibernate框架是目前JavaEE软件开发的企业主流框架,学习Hibernate必然要掌握ORM(对象关系映射Object/Relation Mapping)的概念思想, Hibernate拥有完全的ORM理念,我们在操作数据库时,可以通过面向对象的方式就可以完成数据库的CRUD(创建(Create)、更新(Update)、读取(Read)和删除(Delete))操作。
企业在使用Hibernate进行软件开发进行数据查询时,主要基于HQL(Hibernate 面向对象的查询语言,语法类似SQL)、 Criteria(面向对象的条件查询对象)、SQL(原生态SQL语句)几种方式,本文重点讲解Criteria 这种完全面向对象编程查询方式,详细分析Crieria各种使用与SQL生成关系。
2.Criteria
Criteria 是一个完全面向对象,可扩展的条件查询API,通过它完全不需要考虑数据库底层如何实现、SQL语句如何编写,是Hibernate框架的核心查询对象。
Hibernate 定义了CriteriaSpecification接口规范用来完成面向对象的条件查询,Criteria 就是CriteriaSpecification的子接口。
2.1数据库环境
开发条件:
数据库 oracleXE 10g
用户名 jsd1606
密码 jsd1606
我们以部门(Department)和员工(Employee) 案例,来讲解Criteria的详细使用。
(1).建表:
(a).部门表 department
create table department (
id number(10,0) not null,
name varchar2(255 char),
primary key (id)
)
(b).员工表 employee
create table employee (
id number(10,0) not null,
age number(10,0),
birthday date,
name varchar2(255 char),
department_id number(10,0),
primary key (id)
)
(2).插入数据
(a).部门表 department
insert into department values(1,'人力资源部');
insert into department values(2,'财务部');
insert into department values(3,'行政部');
insert into department values(4,'市场部');
SQL>select * from department; | |
id | name |
1 | 人力资源部 |
2 | 财务部 |
3 | 行政部 |
4 | 市场部 |
(b).员工表 employee
insert into employee values(1,21,TO_DATE('1995-06-06', 'yyyy-MM-dd'),'张三',1);
insert into employee values(2,22,TO_DATE('1994-09-26', 'yyyy-MM-dd'),'李四',1);
insert into employee values(3,21,TO_DATE('1995-03-06', 'yyyy-MM-dd'),'王五',2);
insert into employee values(4,22,TO_DATE('1994-08-21', 'yyyy-MM-dd'),'赵六',3);
mysql>select * from employee; | ||||
id | name | age | birthday | department_id |
1 | 张三 | 21 | 1995-06-06 | 1 |
2 | 李四 | 22 | 1994-09-26 | 1 |
3 | 王五 | 21 | 1995-03-06 | 2 |
4 | 赵六 | 22 | 1994-08-21 | 3 |
2.2.使用JPA注解配置实体类
部门类:
package com.axhu.entity;
import java.util.Set;
import javax.persistence.*;
@Entity
@Table(name="department")
public class Department {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
@Column
private String name;
@OneToMany
@JoinColumn(name="department_id")
private Set<Employee> employees;
}
员工类:
package com.axhu.entity;
import javax.persistence.*;
import java.util.Date;
@Entity
@Table(name="employee ")
public class Employee {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
@Column
private String name;
@Column
private int age;
@Temporal(TemporalType.DATE)
private Date birthday;
@ManyToOne(fetch=FetchType.LAZY)
private Department department;
}
2.3使用Crteria查询所有部门和所有员工
查询所有部门
java代码:
Criteria criteria = ses.createCriteria(Department.class);
List<Department> lists= criteria.list();
产生的SQL:
select id,name from department
查询结果:
人力资源部,财务部,行政部,市场部(以toString方法打印对象,此处简写)
查询所有员工
java代码:
Criteria criteria= ses.createCriteria(Employee.class);
List<Employee> lists= criteria.list();
产生的SQL:
select id,age,birthday,department_id,name from employee
查询结果:
张三,李四,王五,赵六(以toString方法打印对象,此处简写)
总结:
当session.createCriteria(实体类.class) 就会产生一条select所有列from 表;
SQL语句,查询实体类对应数据表的所有记录,然后我们就可以在这个Criteria对象上进行条件查询、分页查询、多表关联查询、投影查询、子查询等一系列操作……
3.Criteria SQL定制详解
3.1对查询添加条件对象Criterion
org.hibernate.criterion.Criterion是Hibernate提供的一个面向对象查询条件接口,一个单独的查询就是Criterion接口的一个实例,用于限制Criteria对象的查询,在Hibernate中Criterion对象的创建通常是通过Restrictions 工厂类完成的。
Restrictions 提供条件查询方法.
查询姓“张”的所有员工信息
java代码:
Criteria criteria= ses.createCriteria(Employee.class);
criteria.add(Restrictions.like("name", "张%"));
List<Employee> lists= criteria.list();
产生的SQL:
(1).select id,age,birthday,department_id,name
from employee where name like ?
(2).select id,name from department where id=?
查询结果:
[Employee [id=1, name=张三, age=21, birthday=1995-06-06,
department=Department [id=1, name=人力资源部]]]
查询年龄大于21的所有员工
java代码:
Criteria criteria= ses.createCriteria(Employee.class);
criteria.add(Restrictions.gt("age", 21));
List<Employee> lists= criteria.list();
产生的主要SQL:
select id.age,birthday,department_id,name
from employee where age>?
查询结果:
李四,赵六(以toString方法打印对象,此处简写)
查询年龄小于28的姓“王”的员工
java代码:
Criteria criteria= ses.createCriteria(Employee.class);
criteria.add(Restrictions.lt("age", 28));
criteria.add(Restrictions.like("name", "王%"));
List<Employee> lists= criteria.list();
产生的主要SQL:
select id,age,birthday,department_id,name
from employee where age<? and name like ?
查询结果:
王五(以toString方法打印对象,此处简写)
总结:
对于多个查询条件,Restrictions提供了逻辑组合查询方法。
and(Criterion lhs, Criterion rhs) 用于生成多个条件and关系SQL语句;
or(Criterion lhs, Criterion rhs) 用于生成多个条件or关系SQL语句;
not(Criterion expression) 用于查询与条件相反的数据,生成not取反查询语句。
3.2分页操作 firstResult和maxResults
Criteria接口提供用于分页查询的方法,实现数据库SQL物理级别的分页操作。
setFirstResult(int firstResult)设置记录的起始位置0代表第一条记录。
setMaxResults(int maxResults)设置查询记录的长度。
查询第1-3条员工记录(共4条记录)
java代码:
Criteria criteria = ses.createCriteria(Employee.class);
criteria.setFirstResult(0);
criteria.setMaxResults(3);
List<Employee> lists= criteria.list();
产生的主要SQL:
select * from
( select id,age,birthday,department_id,name
from employee)
where rownum <= ?
查询结果:
张三,李四,王五(以toString方法打印对象,此处简写)
查询第2-4条员工记录(共3条记录)
java代码:
Criteria criteria= ses.createCriteria(Employee.class);
criteria.setFirstResult(1);
criteria.setMaxResults(3);
List<Employee> lists = criteria.list();
产生的主要SQL:
select * from
( select e.*,rownum rum
from (
select id,age,birthday,department_id,name
from employee
) e
where rownum <= ?
) where rum > ?
查询结果:
李四,王五,赵六(以toString方法打印对象,此处简写)
3.3排序操作 Order
Hibernate提供org.hibernate.criterion.Order用于排序操作,Criteria接口提供addOrder(Order order)用于生成排序SQL.
查询所有员工信息,按照年龄升序(asc)/降序(desc)排列。
java代码:
Criteria criteria= ses.createCriteria(Employee.class);
criteria.addOrder(Order.asc("age"));//Order.desc("age")
List<Employee> lists = criteria.list();
产生的主要SQL:
select id,age,birthday,department_id,name
from employee order by age asc/desc
查询结果:(以toString方法打印对象,此处简写)
asc:王五(21),张三(21),赵六(22),李四(22)
desc:李四(22),赵六(22),王五(21),张三(21)
3.4多表关联操作createAlias和createCriteria
Criteria接口提供createAlias和createCriteria两组方法用于完成多表关联查询.
createAlias(String associationPath, String alias) 采用内连接关联.
createAlias(String associationPath, String alias, int joinType)
可以通过joinType指定连接类型.
createCriteria(String associationPath) 采用内连接关联(返回新的Criteria对象)。
createCriteria(String associationPath, int joinType)
可以通过joinType指定关联类型(返回新的Criteria对象).
查询部门为“人力资源部”的所有员工(使用createCriteria方法)
java代码:
Criteria criteria1 = ses.createCriteria(Employee.class);
Criteria criteria2 = criteria1.createCriteria("department");
criteria2.add(Restrictions.eq("name", "人力资源部"));
List<Employee> lists= criteria.list();
产生的主要SQL:
select e.id,e.age,e.birthday,e.department_id,e.name,d.id,d.name
from employee e
inner join department d on e.department_id=d.id
where d.name=?
查询结果:
张三,李四(以toString方法打印对象,此处简写)
注意事项:
Criteria criteria1和Criteria criteria2一定要是两个对象,不能在一个对象中直接获取.
总结:
代码中的criteria对象,是针对employee表,criteria.createCriteria(”department”) 就是建立employee表和department表的内连接.返回的是针对department表新的criteria2对象,这时再对criteria2 添加条件,就是查询department部门表的属性,而不是employee的属性了.
查询部门为“人力资源部”的所有员工(使用createAlias方法)
java代码:
Criteria criteria = ses.createCriteria(Employee.class);
criteria .createAlias("department","d");
criteria .add(Restrictions.eq("d.name", "人力资源部"));
List<Employee> lists = criteria .list();
产生的主要SQL:
select e.id,e.age,e.birthday,e.department_id,e.name,d.id,d.name
from employee e
inner join department d
on e.department_id=d.id
where d.name=?
查询结果:
张三,李四(以toString方法打印对象,此处简写)
3.5.投影、分组查询 Projection
在实际开发中,进行查询是:可能只需要返回表中的指定列信息(投影)或者进行统计查询(count,avg,sum,min,max).
Criteria接口提供setProjection(Projection projection)方法用于实现投影查询操作.
org.hibernate.criterion.Projections工厂类用于返回Projection投影查询对象.
查询员工表的name和age属性
java代码:
Criteria criteria = ses.createCriteria(Employee.class);
criteria .setProjection(
Projections.projectionList()
.add(Projections.property("name"))
.add(Projections.property("age")));
List<Object> objs= criteria .list();
或:
Criteria criteria = ses.createCriteria(Employee.class);
criteria .setProjection(
Projections.projectionList()
.add(Property.forName("name"))
.add(Property.forName("age")));
List<Object> objs= criteria .list();
产生的主要SQL:
select name,age from employee
查询结果:
Object,Object,Object,Object
查询员工的总数量
java代码:
Criteria criteria = ses.createCriteria(Employee.class);
criteria.setProjection(Projections.rowCount());
Long row = (Long) criteria.uniqueResult();
产生的主要SQL:
select count(*) from employee
查询结果:
4
总结:
rowCount() 查询记录总数量;
count(String propertyName) 统计某列数量;
countDistinct(String propertyName) 统计某列数(排除重复);
avg(String propertyName) 统计某列平均值;
sum(String propertyName) 对某列值求和;
max(String propertyName) 求某列最大值;
min(String propertyName) 求某列最小值;
查询每个部门的员工数量(输出部门的编号和数量)
java代码:
Criteria criteria = ses.createCriteria(Employee.class);
criteria.setProjection(
Projections.projectionList()
.add(Projections.groupProperty("department"))
.add(Projections.count("id")));
List<Object> lists = criteria.list();
产生的主要SQL:
select e.department_id,count(e.id)
from employee e group by e.department_id
查询结果:
Object,Object,Object
3.6. 设置结果集封装策略 ResultTransformer
刚刚说了投影操作的使用,其实在Hibernate内部投影查询是会影响到结果集的封装策略的.先用HQL举例说明:
session.createQuery(”from Employee”).list();//返回 List
session.createQuery(”select count(e) from Employee e”).list();//返回List
session.createQuery(”select name,age from Employee”).list();//返回List
从这几个例子我们不难发现,如果没有指定select语句(没有投影),那么将返回表中的所有字段,返回结果会被封装到Entity实体对象Employee中,一但提供select语句(投影)后,返回的结果类型,将不再封装到Employee对象,而是根据投影的实际类型返回,这就是投影对结果封装策略的影响.
我们再来看之前写过的Criteria 查询案例:
session.createCriteria(Employee.class).list();//返回 List
session.createCriteria(Employee.class).setProjection(
Projections.projectionList()
.add(Property.forName(”name”))
.add(Property.forName(”age”)));//返回 List
投影之后,返回的结果将不再被封装到Employee对象中,这是为什么呢?
我们一起来看看 Criteria的接口定义,不难发现在Criteria接口中提供了一个setResultTransformer(ResultTransformer resultTransformer),这个ResultTransformer就是结果集转换策略接口,在Criteria的父接口中CriteriaSpecification定义了几个ResultTransformer的常用实现.
ALIAS_TO_ENTITY_MAP 将结果集封装到Map对象;
ROOT_ENTITY 将结果集封装到根实体对象;
DISTINCT_ROOT_ENTITY 将结果集封装到不重复的根实体对象;
PROJECTION 根据投影的结果类型自动封装;
当进行投影查询时,结果的封装策略由ROOT_ENTITY 变为了PROJECTION,所以是根据查询数据进行封装,而不是封装到根对象.
了解上面原理后,即使只查询name和age属性,也可以封装成List集合.
使用AliasToBeanResultTransformer 修改结果封装策略,AliasToBeanResultTransformer 会根据返回列自动匹配类中属性名,完成封装
java代码:
Criteria criteria = ses.createCriteria(Employee.class);
criteria.setProjection(
Projections.projectionList()
.add(Projections.property("name").as("name"))
.add(Projections.property("age").as("age")));
criteria.setResultTransformer(
new AliasToBeanResultTransformer(Employee.class));
List<Employee[]> lists = criteria.list();
产生的主要SQL:
select e.name,e.age from employee
查询结果:
[Employee [id=null, name=张三, age=21, birthday=null, department=null],
Employee [id=null, name=李四, age=22, birthday=null, department=null],
Employee [id=null, name=王五, age=21, birthday=null, department=null],
Employee [id=null, name=赵六, age=22, birthday=null, department=null]]
4.附录
测试代码:
@Test
public void test() throws HibernateException{
Session ses = null;
Transaction tx = null;
try {
ses = SessionUtil.getSession();
tx = ses.beginTransaction();
Criteria criteria = ses.createCriteria(Department.class);
List<Department> lists= criteria.list();
System.out.println(lists);
tx.commit();
} catch (HibernateException e) {
// TODO Auto-generated catch block
if(null!=tx){
tx.rollback();
}
e.printStackTrace();
}finally{
SessionUtil.closeSession(ses);
}
}
工具类:
public class SessionUtil {
private static SessionFactory sf = null;
/**
* 通过静态代码块来创建SessionFactory
*/
static{
try {
//1.启动hibernate.cfg.xml配置文件,通过Configuration类
Configuration cfg = new Configuration();
//2.调用configure()方法来去加载src根目录下面的hibernate.cfg.xml文件
//注意:configure(String path);//重载方法,用来加载指定目录下的配置文件.
cfg.configure();
//3.创建SessionFactory(充当数据源的一个代理,重量级的对象)
//通常一个数据库只需要配置一个SesisonFactory对象.通过这个
//对象,可以获取Session(Connection+Cache)
//3-1.hibernate3.x创建方式(已经过时了)
//SessionFactory sf = cfg.buildSessionFactory();
//3-2.hibernate4.x创建方式
ServiceRegistry sr = new StandardServiceRegistryBuilder()
.applySettings(cfg.getProperties()).build();
sf = cfg.buildSessionFactory(sr);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 获取Session
*/
public static Session getSession() throws HibernateException{
return sf==null?null:sf.openSession();
}
/**
* 关闭session
*/
public static void closeSession(Session ses){
if(null!=ses){
ses.close();
}
}
}
说明:该文系参考网上,现找不到该原文(我没找到).特此说明,向原博主致敬
author : ily
address : anhui hefei
email:@foxmail.com
出自博客 http://www.cnblogs.com/yunlei0821/,欢迎留言补充指正,仅供互相交流学习.转载请务必保留此出处..
本文为作者原创 ,欢迎转载和收藏,转载请保留作者及出处,谢谢您的配合,如有侵权,请第一时间联系 yunlei0821@vip.qq.com,以便及时删除...
晨露&无尘