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;
idname
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;
idnameagebirthdaydepartment_id
1张三211995-06-061
2李四221994-09-261
3王五211995-03-062
4赵六221994-08-213
 

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
 
 

转载于:https://www.cnblogs.com/yunlei0821/p/6601776.html