目录

  • ​​目录​​
  • ​​前言​​
  • ​​OID查询​​
  • ​​对象导航查询​​
  • ​HQL查询​
  • ​​查询所有​​
  • ​​条件查询​​
  • ​​排序查询​​
  • ​​分页查询​​
  • ​​投影查询​​
  • ​​聚合函数的使用​​
  • ​QBC查询​
  • ​​查询所有​​
  • ​条件查询​
  • ​​Restrictions类的常用方法​​
  • ​排序查询​
  • ​​Order类的常用方法​​
  • ​​分页查询​​
  • ​统计查询​
  • ​​Projections类的常用方法​​
  • ​​离线查询​​
  • ​多表查询​
  • ​​MySQL中的多表查询​​
  • ​HQL中的多表查询​
  • ​​内连接​​
  • ​​迫切内连接​​
  • ​​左外连接​​
  • ​​迫切左外连接​​
  • ​​右外连接​​
  • ​Hibernate检索策略​
  • ​​检索策略的概念​​
  • ​关联级别延迟操作​
  • ​​set集合上的fetch和lazy​​
  • ​​批量抓取​​


前言

  以下案例的项目上下文在《​​重拾Hibernate框架——一对多关联​​》、《​​重拾Hibernate框架——多对多关联​​》这两篇文章中提及,如无特殊说明,本文中提及的案例将基于上述项目进行运行测试。

OID查询

  根据ID查询记录,调用session中的get方法进行实现。如:

User user = session.get(User.class, 1);

对象导航查询

  查询某个公司中所有员工的过程,使用对象导航实现。对象导航查询适用于单向一对多关系下通过“一方”获取“多方”的信息记录。如:

// 获取ID为3的公司的所有员工
Company company = session.get(Company.class, 3);
Set<Employee> employees = company.getEmployees();
System.out.println(employees.size());

HQL查询

  HQL(HQL,Hibernate Query Language),是Hibernate提供的一种查询语言,HQL语法类似于SQL,不同的是HQL是一种完全面向对象的语言,能够直接查询实体类及属性。而SQL语句操作的是数据库中的表和表中的字段。

查询所有

  如查询所有公司记录:

Query query = session.createQuery("from Company");
List<Company> list = query.list();
for (Company company : list) {
System.out.println(company.getId() + "=" + company.getName());
}

条件查询

  where

// Query query = session.createQuery("from Company where id = ? and name = ?"); // 可以取别名,如下
Query query = session.createQuery("from Company as c where c.id = ? and c.name = ?");
// 设置占位符(?)的值,从位置0开始设置
query.setParameter(0, 1);
query.setParameter(1, "CSDN");
List<Company> list = query.list();

  like

Query query = session.createQuery("from Company where name like ?");
query.setParameter(0, "%D%");
List<Company> list = query.list();

排序查询

// 升序(默认)
// Query query = session.createQuery("from Company order by id asc");
// 降序
Query query = session.createQuery("from Company order by id desc");
List<Company> list = query.list();

分页查询

(1)MySQL中实现分页:使用关键字limit实现
如:

SELECT * FROM t_company LIMIT 0, 3

(2)HQL中实现分页
  在HQL语句中,不支持limit关键字(因为MySQL数据库支持limit关键字,但其他数据库不一定支持)。在Hibernate的Query对象中封装了两个方法实现了分页操作。如:

Query query = session.createQuery("from Company");
// 设置分页的开始位置
query.setFirstResult(0);
// 设置每页的记录数
query.setMaxResults(3);
List<Company> list = query.list();

投影查询

  投影查询:查询部分字段的值,而不是查询所有字段的值,这就是投影查询。
  注:在HQL语句中select语句后面不能写“*”,不支持这样查询所有数据。
(1)查询单个字段的值

// 查询单个字段的值
Query query = session.createQuery("select id from Company");
List<Object> list = query.list();
for (Object obj : list) {
System.out.println(obj);
}

(2)查询多个字段的值

// 查询多个字段的值
Query query = session.createQuery("select id, name from Company");
List<Object[]> list = query.list();
for (Object[] obj : list) {
System.out.println(obj[0] + "=" + obj[1]);
}

聚合函数的使用

  常见的聚合函数:count、sum、avg、max、min。
  这里以count为例,查询公司的总记录数,如:

Query query = session.createQuery("select count(*) from Company");
Object obj = query.uniqueResult();
// 直接输出总记录数
System.out.println(obj);
Query query = session.createQuery("select count(*) from Company");
Object obj = query.uniqueResult();
// 转成int输出
Long longObj = (Long) obj;
int count = longObj.intValue();
System.out.println(count);

  这里需要注意的是,不能直接将obj转成int类型:

Query query = session.createQuery("select count(*) from Company");
Object obj = query.uniqueResult();
// 错误方式
int count = (int) obj;
System.out.println(count);

  否则会报:​​java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer​​ 错误。

QBC查询

  QBC查询,即Query By Criteria查询,使用Criteria对象进行实现。
  使用QBC查询,一般需要以下三个步骤:
1. 使用Session实例 的createCriteria()方法创建Criteria对象;
2. 使用工具类Restrictions的方法为Criteria对象设置查询条件,Order工具类的方法设置排序方式,Projections工具类的方法进行统计和分组;
3. 使用Criteria对象的list()方法进行查询并返回结果。

查询所有

Criteria criteria = session.createCriteria(Company.class);
List<Company> list = criteria.list();
for (Company company : list) {
System.out.println(company.getId() + "=" + company.getName());
}

条件查询

使用工具类Restrictions的方法为Criteria对象设置查询条件。

  where

Criteria criteria = session.createCriteria(Company.class);
// 添加查询条件
// 在add方法中使用类的方法实现条件的设置
criteria.add(Restrictions.eq("id", 1));
criteria.add(Restrictions.eq("name", "CSDN"));
List<Company> list = criteria.list();
for (Company company : list) {
System.out.println(company.getId() + "=" + company.getName());
}

  like

Criteria criteria = session.createCriteria(Company.class);
// 添加查询条件
criteria.add(Restrictions.like("name", "%D%"));
List<Company> list = criteria.list();

Restrictions类的常用方法

方法名称

描述

Restrictions.eq

等于 =

Restrictions.allEq

使用Map,使用key/value进行多个等于的判断

Restrictions.gt

大于 >

Restrictions.ge

大于等于 >=

Restrictions.lt

小于 <

Restrictions.le

小于等于 <=

Restrictions.between

对应SQL的between子句

Restrictions.like

对应SQL的like子句

Restrictions.in

对应SQL的in子句

Restrictions.and

and关系

Restrictions.or

or关系

Restrictions.sqlRestriction

SQL限定查询

排序查询

  调用addOrder设置排序规则,即对哪个属性采用升序还是降序排序,如:

// 升序(默认)
/*Criteria criteria = session.createCriteria(Company.class);
criteria.addOrder(Order.asc("id"));*/
// 降序
Criteria criteria = session.createCriteria(Company.class);
criteria.addOrder(Order.desc("id"));
List<Company> list = criteria.list();

Order类的常用方法

方法名称

描述

Order.asc

升序

Order.desc

降序

分页查询

Criteria criteria = session.createCriteria(Company.class);
// 设置分页的开始位置
criteria.setFirstResult(0);
// 设置每页的记录数
criteria.setMaxResults(3);
List<Company> list = criteria.list();
for (Company company : list) {
System.out.println(company.getId() + "=" + company.getName());
}

统计查询

使用Projections工具类的方法进行统计和分组。

Criteria criteria = session.createCriteria(Company.class);
criteria.setProjection(Projections.rowCount());
Object obj = criteria.uniqueResult();
Long longObj = (Long) obj;
int count = longObj.intValue();
System.out.println(count);

Projections类的常用方法

方法名称

描述

Projections.avg

求平均值

Projections.count

统计某属性的数量

Projections.countDistinct

统计某属性不同值的数量

Projections.groupProperty

指定某个属性为分组属性

Projections.max

求最大值

Projections.min

求最小值

Projections.projectionList

创建一个ProjectionList对象

Projections.rowCount

查询结果集中的记录条数

Projections.sum

求某属性的合计

离线查询

  通常离线查询可以在表示层建立,然后传入业务层进行查询。

DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Company.class);
// 最终执行时才需要到session对象
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Company> list = criteria.list();
for (Company company : list) {
System.out.println(company.getId() + "=" + company.getName());
}

多表查询

MySQL中的多表查询

(1)内连接

/*内连接*/
SELECT * FROM t_company c, t_employee e WHERE c.id = e.cid

SELECT * FROM t_company c INNER JOIN t_employee e ON c.id = e.cid

(2)左外连接

/*左外连接*/
SELECT * FROM t_company c LEFT OUTER JOIN t_employee e ON c.id = e.cid

(3)右外连接

/*右外连接*/
SELECT * FROM t_company c RIGHT OUTER JOIN t_employee e ON c.id = e.cid

HQL中的多表查询

内连接

  语法:​​from 实体类名称 别名 inner join 别名.存储另一个实体类对象的set属性名称​​。如:

Query query = session.createQuery("from Company c inner join c.employees");
List<Object[]> list = query.list();
for (Object[] objs: list) {
Company company = (Company) objs[0];
Employee employee = (Employee) objs[1];
System.out.println(company.getId() + ": " + company.getName() + " " + employee.getId() + ": " + employee.getName());
}

  调用query对象的list方法后,方法返回的是一个List集合,集合中的每个部分是数组形式。

迫切内连接

  迫切内连接本质上也是内连接操作,不同的是使用内连接返回list中每部分是数组,而迫切内连接返回list中每部分是对象。
  语法:​​​from 实体类名称 别名 inner join fetch 别名.存储另一个实体类对象的set属性名称​​。如:

Query query = session.createQuery("from Company c inner join fetch c.employees");
List<Company> list = query.list();
for (Company c : list) {
System.out.println(c.getId() + ": " + c.getName());
}

左外连接

  语法:​​from 实体类名称 别名 left outer join 别名.存储另一个实体类对象的set属性名称​​。如:

Query query = session.createQuery("from Company c left outer join c.employees");
List<Object[]> list = query.list();
for (Object[] objs: list) {
Company company = (Company) objs[0];
if(objs[1] != null) {
Employee employee = (Employee) objs[1];
System.out.println(company.getId() + ": " + company.getName() + " " + employee.getId() + ": " + employee.getName());
} else {
System.out.println(company.getId() + ": " + company.getName() + " null : null");
}
}

  调用query对象的list方法后,方法返回的是一个List集合,集合中的每个部分是数组形式。

迫切左外连接

  迫切左外连接本质上也是左外连接操作,不同的是使用左外连接返回list中每部分是数组,而迫切左外连接返回list中每部分是对象。
  语法:​​​from 实体类名称 别名 left outer join fetch 别名.存储另一个实体类对象的set属性名称​​。如:

Query query = session.createQuery("from Company c left outer join fetch c.employees");
List<Company> list = query.list();
for (Company c : list) {
System.out.println(c.getId() + ": " + c.getName());
}

右外连接

  语法:​​from 实体类名称 别名 right outer join 别名.存储另一个实体类对象的set属性名称​​。如:

Query query = session.createQuery("from Company c right outer join c.employees");
List<Object[]> list = query.list();
for (Object[] objs: list) {
Employee employee = (Employee) objs[1];
if(objs[0] != null) {
Company company = (Company) objs[0];
System.out.println(company.getId() + ": " + company.getName() + " " + employee.getId() + ": " + employee.getName());
} else {
System.out.println("null : null " + employee.getId() + ": " + employee.getName());
}
}

注:没有迫切右外连接!!!

Hibernate检索策略

检索策略的概念

  在Hibernate中检索策略分为两类:
第一类 立即查询:调用查询方式将立即发送查询语句到数据库进行查询,如根据ID调用get方法查询,会立即发送查询到数据库。如:

// 获取ID为3的公司的所有员工
// 调用get方法将立即发送SQL语句查询数据库
Company company = session.get(Company.class, 3);
System.out.println(company.getId() + ": " + company.getName());

第二类 延迟查询:调用方法时不会立即发送查询语句到数据库进行查询,而是在真正需要到数据时才发送语句查询数据库,如根据ID调用load方法查询,调用load方法不会马上发送语句查询数据库。如:

// 获取ID为3的公司的所有员工
// 调用load方法之后,不会立即发送SQL语句到数据库进行查询
Company company = session.load(Company.class, 3);
// 返回对象ID时,不会发送SQL语句进行查询
System.out.println(company.getId());
// 得到对象其他属性值时才发送查询语句
System.out.println(company.getName());

  注:在以上代码测试过程中,发现没有延迟查询的效果,原因不明,待查明!!!
  延迟查询又分为两类:
(1)类级别延迟:如根据ID查询返回实体类对象,调用load方法不会立即发送语句到数据库进行查询。
(2)关联级别延迟:如查询某个公司的所有员工记录的过程。查询到某公司后得到公司的实体类对象,该对象拥有包含所有员工信息的set集合属性,在未使用该属性内的数据时,并不会发送语句到数据库查询员工信息。如:

// 根据ID查询公司,这里并没有自动查询该公司的所有员工
Company company = session.get(Company.class, 3);

// 下面这一句将发送查询语句到数据库进行查询
Set<Employee> employees = company.getEmployees();

System.out.println("test");
System.out.println(employees.size());

关联级别延迟操作

​<set>​​集合上的fetch和lazy

  在映射文件中可以配置实现关联级别延迟的方式。如在Company.hbm.xml中配置:
在set标签上使用属性fetch和lazy:

  • fetch:控制的是查询其关联对象的时候采用的SQL语句的格式。
  • select:默认值。发送一条select语句查询其关联对象。
  • join:发送一条迫切做外连接查询关联对象。
  • subselect:发送一条子查询查询其关联对象。
  • lazy:控制的是查询其关联对象的时候是否采用延迟加载的策略。
  • true:默认值。默认查询关联对象的时候采用延迟加载。
  • false:查询关联对象的时候不采用延迟加载。
  • extra:极其懒惰,查询关联对象的时候,采用比延迟加载更懒惰的方式进行查询。如需要得到记录的总条数,其只会使用count聚合函数查询记录总数。

测试:配置Company.hbml映射文件的set标签
(1)​​​<set name="employees" fetch="select" lazy="true">​

// 根据ID查询公司,这里并没有自动查询该公司的所有员工
Company company = session.get(Company.class, 3);

// 下面这一句将发送select查询语句到数据库进行查询
Set<Employee> employees = company.getEmployees();

System.out.println("test");
System.out.println(employees.size());

(2)​​<set name="employees" fetch="select" lazy="false">​

// 根据ID查询公司时也查询了该公司的所有员工信息
Company company = session.get(Company.class, 3);

Set<Employee> employees = company.getEmployees();

System.out.println("test");
System.out.println(employees.size());

(3)​​<set name="employees" fetch="select" lazy="extra">​

// 根据ID查询公司,这里并没有自动查询该公司的所有员工
Company company = session.get(Company.class, 3);

// 只发送查询记录总数的语句到数据库,如下:
Set<Employee> employees = company.getEmployees();

System.out.println("test");
System.out.println(employees.size());

  获取员工记录时,发送的SQL语句:

Hibernate: 
select
count(id)
from
t_employee
where
cid =?

批量抓取

  如,默认情况下,查询所有公司下的所有员工信息。我们可以先查询所有公司记录再遍历每个公司获取公司下的所有员工信息。在关联级别延迟加载下,只有公司去获取员工信息时才会发送查询语句进行查询。在这样的情况下,Hibernate会根据公司记录逐条发送查询员工记录的语句到数据库,影响效率。如:

Criteria criteria = session.createCriteria(Company.class);
List<Company> list = criteria.list();
for (Company company : list) {
  // 每次遍历才发送查询当前公司下的所有员工记录信息
Set<Employee> employees = company.getEmployees();
for (Employee employ : employees) {
System.out.println(company.getId() + ": " + company.getName() + " " + employ.getId() + ": " + employ.getName());
}
}

  通过配置公司Company.hbm.xml中set标签的batch-size值,可以减少发送查询语句的次数,批量查询。如:

<set name="employees" batch-size="10">

  这时遍历一次后,会发现类似如下查询,使用in帮我们查询了后续多条员工记录信息,而不是一条一条的查询:

Hibernate: 
select
employees0_.cid as cid5_1_1_,
employees0_.id as id1_1_1_,
employees0_.id as id1_1_0_,
employees0_.name as name2_1_0_,
employees0_.gender as gender3_1_0_,
employees0_.phone as phone4_1_0_,
employees0_.cid as cid5_1_0_
from
t_employee employees0_
where
employees0_.cid in (
?, ?, ?
)