Hibernate学习笔记|Hibernate学习笔记 | 详解Hibernate检索方式(HQL,QBC等)
Hibernate检索方式
Hibernate提供了以下几种检索对象的方式
- 导航对象图检索方式:根据已经加载的对象导航到其他对象
例如前面我们学到的通过Customer
类来获取集合的方式来获取Order
对象 - OID检索方式:按照对象的OID来检索对象
- HQL检索方式:使用面向对象的HQL查询语言
- QBC检索方式:使用QBC API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口。
- 本地SQL检索方式:使用本地数据库的SQL查询语句。
- 在查询语句中设定各种查询条件
- 支持投影查询,即仅检索出对象的部分属性
- 支持分页查询
- 支持连接查询
- 支持分组查询,运行使用HAVING和GROUP BY关键字
- 提供内置聚集函数,如
sum()
,min()
,max()
- 支持子查询
- 支持动态绑定参数
- 能够调用用户定义的SQL函数或标准的SQL函数
- 通过Session的
createQuery()
创建一个Query
对象,它包括一个HQL查询语句,HQL查询语句中可以包含命名参数。 - 动态绑定参数
- 调用
Query
的相关方法执行查询语句
setParameter()
方法返回自身实例,而不是void
类型绑定参数
- Hibernate的参数绑定机制依赖于JDBC API中的PreparedStatement的预定义SQL语句功能。
- HQL的参数绑定有两种形式
按参数名字绑定:在HQL查询语句中定义命名参数,命名参数以:
开头。
按参数位置绑定:在HQL查询语句中用?编号
来定义参数位置。 - 相关方法
setParameter()
:绑定任意类型的参数,该方法的第三个参数显式指定Hibernate映射类型。
setEntity()
:把参数与一个持久化类绑定。但该方法在Hibernate5中已过时,推荐使用第一个方法setParameter()
。 - HQL采用ORDER BY关键字对查询结果排序。
department
表和employees
表为一对多关系。文章图片
文章图片
两个实体类如下:
package com.cerr.hibernate.entities;
import java.util.Set;
public class Department {
private Integer id;
private String name;
private Set emps;
public Integer getId() {
return id;
}public void setId(Integer id) {
this.id = id;
}public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}public Set < Employee > getEmps() {
return emps;
}public void setEmps(Set < Employee > emps) {
this.emps = emps;
}}
package com.cerr.hibernate.entities;
public class Employee {
private Integer id;
private String name;
private float salary;
private String email;
private Department dept;
public Integer getId() {
return id;
}public void setId(Integer id) {
this.id = id;
}public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}public float getSalary() {
return salary;
}public void setSalary(float salary) {
this.salary = salary;
}public String getEmail() {
return email;
}public void setEmail(String email) {
this.email = email;
}public Department getDept() {
return dept;
}public void setDept(Department dept) {
this.dept = dept;
}@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", email='" + email + '\'' +
", dept=" + dept +
'}';
}
}
可以使用
?编号
来作为参数:@org.junit.Test
public void test(){
//创建Query对象
String hql = "FROM Employee WHERE salary > ?0 AND email LIKE ?1 ";
Query query = session.createQuery(hql);
Department dept = session.get(Department.class,1);
//绑定参数
query.setParameter(0,(float)6000).setParameter(1,"%9%");
//查询
List emps = query.list();
System.out.println(emps.size()+":"+emps);
}
也可以使用
:标识符
作为参数,还可以将实体类也作为参数(前提是存在映射关系)@org.junit.Test
public void test(){
//创建Query对象
String hql = "FROM Employee WHERE salary > :sal AND dept = :d AND email LIKE :ema ";
Query query = session.createQuery(hql);
Department dept = session.get(Department.class,1);
//绑定参数
query.setParameter("sal",(float)6000).setParameter("ema","%%").setParameter("d",dept);
//查询
List emps = query.list();
System.out.println(emps.size()+":"+emps);
}
分页查询
setFirstResult(int firstResult)
设定从哪个对象开始检索,参数firstResult
表示这个对象在查询结果中的索引位置,索引位置的起始值为0,默认情况下,Query
从查询结果中的第一个对象开始检索
setMaxResults(int maxResults)
设定一次最多检索出的对象的数目。在默认情况下,Query
和Criteria
接口检索出查询结果中所有的对象。
@org.junit.Test
public void testPageQuery(){
String hql = "FROM Employee";
Query query = session.createQuery(hql);
int pageNo = 3;
//页数
int pageSize = 2;
//每页最大数量
//查询第pageNo页的pageSize条记录
List employees = query.setFirstResult((pageNo-1)*pageSize).setMaxResults(pageSize).list();
System.out.println(employees);
}
在映射文件中定义命名查询语句
- Hibernate允许在映射文件中定义字符串形式的查询语句。
-
元素用于定义一个HQL查询语句,它和
元素并列。 - 在程序中通过
Session
的getNameQuery()
获取查询语句对应的Query
对象。
Employee.hbm.xml
文件中定义命名查询语句 :minSalary AND id < :maxID ]]>
。然后测试类代码:
@org.junit.Test
public void testNameQuery(){
Query query = session.getNamedQuery("salaryEmps");
List employees = query.setParameter("minSalary",(float)5000).setParameter("maxID",4).list();
System.out.println(employees);
}
投影查询
- 查询结果仅包含实体的部分属性,可以通过SELECT关键字实现。
Query
的list()
返回的集合中包含的是数组类型的元素,每个对象数组代表查询结果的一条记录。
- 可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录。
- 可以通过DISTINCT关键字来保证查询结果不会返回重复元素。
/**
* 返回一个对象的集合
*/
@org.junit.Test
public void testFieldQuery2(){
String hql = "SELECT new Employee(salary,email) FROM Employee WHERE dept = :dept ";
Query query = session.createQuery(hql);
Department dept = new Department();
dept.setId(1);
List result = query.setParameter("dept",dept).list();
//打印
for(Employee emp : result){
System.out.println(emp.getEmail());
}
}/**
* 返回一个字符串的集合
*/
@org.junit.Test
public void testFieldQuery(){
String hql = "SELECT email,salary FROM Employee WHERE dept = :dept ";
Query query = session.createQuery(hql);
Department dept = new Department();
dept.setId(1);
List
注意在使用返回对象集合的时候,在实体类中需要有对应的构造器方法,并且如果加的是有参的构造器,那么一定要加上一个无参构造器。
报表查询 报表查询用于对数据分组和统计,与SQL一样,HQL利用GROUP BY关键字对数据分组,用HAVING关键字对分组数据设定约束条件。
在HQL查询语句中可以调用以下的聚集函数
count()
min()
max()
sum()
avg()
@org.junit.Test
public void testGroupBy(){
String hql = "SELECT min(salary),max(salary) FROM Employee GROUP BY dept HAVING min(salary) >:minSal";
Query query = session.createQuery(hql).setParameter("minSal",(float)5000);
List
HQL的(迫切)左外连接 迫切左外连接
-
LEFT JOIN FETCH
关键字表示迫切左外连接检索策略 -
list()
返回的集合中存放实体对象的引用。每个Department
对象关联的Employee
集合都被初始化,存放所有关联的Employee
的实体对象。 - 查询结果中可能会包含重复元素,可以通过一个HashSet来过滤重复元素。也可以在查询语句中加入
distinct
关键字。
-
LEFT JOIN
关键字表示左外连接查询 list()
返回的集合中存放的是对象数组类型- 根据配置文件来决定
Employee
集合的检索策略 - 如果希望
list()
返回的集合仅仅包含Department
对象,可以在HQL查询语句中使用SELECT
关键字。
//左外连接
@org.junit.Test
public void testLeftJoin(){
String hql = "SELECT distinct d FROM Department d LEFT JOIN d.emps";
Query query = session.createQuery(hql);
//加上SELECT后
List res = query.list();
for (Department department : res){
System.out.println(department);
}
//没加SELECT
//List
HQL(迫切)内连接 迫切内连接
-
INNER JOIN FETCH
关键字表示迫切内连接,也可以省略INNER关键字 -
list()
返回的集合中存放实体类(Department
)对象的引用,每个实体类对象的关联的集合(Employee
集合)都被初始化,存放所有关联的Employee
对象。
-
INNER JOIN
关键字表示内连接,也可以省略INNER关键字 -
list()
的集合中存放的每个元素对应查询结果的一条记录,每个元素都是对象数组类型 - 如果希望
list()
返回的集合仅仅包含Department
对象,可以在HQL查询语句中使用SELECT
关键字。
//内连接
@org.junit.Test
public void testLeftJoin(){
String hql = "SELECT distinct d FROM Department d INNER JOIN d.emps";
Query query = session.createQuery(hql);
//加上SELECT后
List res = query.list();
for (Department department : res){
System.out.println(department);
}
//没加SELECT
//List
内连接和左外连接的区别是内连接不会返回左表不符合条件的记录,而左外连接会返回左表中不符合条件的记录,比如下面的表中
文章图片
表的记录只有DEPT_ID为1的,而在
departments
表中有两条记录:
文章图片
那么如果用上面的左外连接的代码查询
Department
则会查到两个部门(其中第二个部门为空),如果用上面的内连接的代码查询
Department
则会查询到一个部门。
QBC检索 QBC查询就是通过使用Hibernate提供的Query By Criteria API来查询对象,这种API封装了SQL语句的动态拼装,对查询提供了更加面向对象的功能接口。
下面我们来讲下几个例子,假设有
Employees
表如下:文章图片
其实体类如下:
package com.cerr.hibernate.entities;
public class Employee {
private Integer id;
private String name;
private float salary;
private String email;
private Department dept;
public Integer getId() {
return id;
}public void setId(Integer id) {
this.id = id;
}public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}public float getSalary() {
return salary;
}public void setSalary(float salary) {
this.salary = salary;
}public String getEmail() {
return email;
}public void setEmail(String email) {
this.email = email;
}public Department getDept() {
return dept;
}public void setDept(Department dept) {
this.dept = dept;
}@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", email='" + email + '\'' +
", dept=" + dept +
'}';
}public Employee(float salary, String email) {
this.salary = salary;
this.email = email;
}public Employee() {
}
}
初始化与关闭:
package com.cerr.hibernate.entities;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.*;
import org.hibernate.query.Query;
import org.junit.After;
import org.junit.Before;
import java.util.Arrays;
import java.util.List;
public class Test {
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
@Before
public void init() throws Exception {
Configuration configuration = new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
session = sessionFactory.openSession();
transaction = session.beginTransaction();
}@After
public void destory() throws Exception {
transaction.commit();
session.close();
sessionFactory.close();
}}
- QBC使用
Criteria
对象来进行检索,可以使用其add()
来添加查询条件,使用uniqueResult()
或list()
来查询结果。
例如单元测试如下:
@org.junit.Test
public void testQBC(){
//创建Criteria对象
Criteria criteria = session.createCriteria(Employee.class);
//添加查询条件
criteria.add(Restrictions.eq("email","9"));
criteria.add(Restrictions.gt("salary",(float)5000));
//执行查询
Employee employee = (Employee) criteria.uniqueResult();
System.out.println(employee);
}
- 在QBC中要添加
AND
,可以使用Conjunction
对象,要添加OR
,可以使用Disjunction
对象。
单元测试如下:
@org.junit.Test
public void testQBC2(){
Criteria criteria = session.createCriteria(Employee.class);
//AND:使用Conjunction表示,而其本身就是一个Criteria对象。
//且其中还可以添加Criteria对象
Conjunction conjunction = Restrictions.conjunction();
conjunction.add(Restrictions.like("name","a"));
Department dept = new Department();
dept.setId(1);
conjunction.add(Restrictions.eq("dept",dept));
System.out.println(conjunction);
//OR
Disjunction disjunction = Restrictions.disjunction();
disjunction.add(Restrictions.ge("salary",(float)6000));
disjunction.add(Restrictions.isNull("email"));
criteria.add(disjunction);
criteria.add(conjunction);
criteria.list();
}
发送的SQL查询语句如下:
select
this_.ID as ID1_2_0_,
this_.NAME as NAME2_2_0_,
this_.SALARY as SALARY3_2_0_,
this_.EMAIL as EMAIL4_2_0_,
this_.DEPT_ID as DEPT_ID5_2_0_
from
EMPLOYEES this_
where
(
this_.SALARY>=?
or this_.EMAIL is null
)
and (
this_.NAME like ?
and this_.DEPT_ID=?
)
- 在QBC中使用统计查询,可以通过
Projections
的静态方法实现。
查询salary
的最大值:
@org.junit.Test
public void testQBC3(){
Criteria criteria = session.createCriteria(Employee.class);
//统计查询:可以由Projections的静态方法得到
criteria.setProjection(Projections.max("salary"));
System.out.println(criteria.uniqueResult());
}
- 排序可以使用
Order
类的静态方法,分页可以使用Criteria
的方法。
示例:
@org.junit.Test
public void testQBC4(){
Criteria criteria = session.createCriteria(Employee.class);
//添加排序
criteria.addOrder(Order.asc("salary"));
//升序
criteria.addOrder(Order.desc("email"));
//降序
//设置翻页
int pageSize = 2;
int pageNo = 3;
criteria.setFirstResult((pageNo-1)*pageSize).setMaxResults(pageSize).list();
}
本地SQL查询 本地SQL查询来完善HQL不能涵盖所有的查询特性。
示例:
@org.junit.Test
public void testNativeSQL(){
String sql = "INSERT INTO departments VALUES(?0,?1)";
Query query = session.createSQLQuery(sql);
query.setParameter(0,3).setParameter(1,"c").executeUpdate();
}
推荐阅读
- EffectiveObjective-C2.0|EffectiveObjective-C2.0 笔记 - 第二部分
- 由浅入深理解AOP
- 继续努力,自主学习家庭Day135(20181015)
- python学习之|python学习之 实现QQ自动发送消息
- Android中的AES加密-下
- 一起来学习C语言的字符串转换函数
- 定制一套英文学习方案
- 漫画初学者如何学习漫画背景的透视画法(这篇教程请收藏好了!)
- 《深度倾听》第5天──「RIA学习力」便签输出第16期
- 如何更好的去学习