Hibernate中提供了多种检索对象的方式,主要包括以下种类:
- 导航对象图检索方式:根据已经加载的对象导航到其他对象
- OID检索方式:根据对象的OID来检索对象
- HQL检索方式:使用面向对象的HQL查询语言
- QBC检索方式:使用QBC(Query By Criteria)API来检索对象。这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口
- 本地SQL检索方式:使用本地数据库的SQL查询语句
本文主要介绍第三种方式,也就是HQL检索对象。
HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似。在Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。它有如下功能:
- 在查询语句中设定各种查询条件
- 支持投影查询,即仅检索出对象的部分属性
- 支持分页查询
- 支持连接查询
- 支持分组查询,允许使用HAVING和GROUP BY关键字
- 提供内置聚集函数,如sum(), min()和max()
- 支持子查询
- 支持动态绑定参数
- 能够调用用户定义的SQL函数或标准的SQL函数
HQL检索方式包括以下步骤:
- 通过Session的createQuery()方法创建一个Query对象,它包括一个HQL查询语句,HQL查询语句可以包含命名参数
- 动态绑定参数
- 调用Query相关方法执行查询语句
Query接口支持方法链编程风格,它的setXxx()方法返回自身实例,而不是void类型。
HQL vs SQL:
- HQL查询语句是面向对象的,Hibernate负责解析HQL查询语句,然后根据对象-关系映射文件中的映射信息,把HQL查询语句翻译成相应的SQL语句,HQL查询语句中的主体是域模型中的类及类的属性
- SQL查询语句是与关系数据库绑定在一起的。SQL查询语句中的主体是数据库表及表的字段
绑定参数:
- Hibernate的参数绑定机制依赖于JDBC API中的PreparedStatement的预定义SQL语句功能
- Hibernate的参数绑定有两种形式:
1.按参数名字绑定:在HQL查询语句中定义命名参数,命名参数以":"开头
2.按参数位置绑定:在HQL查询语句中用"?"来定义参数位置
- 相关方法:
1.setEntity():把参数与一个持久化类绑定
2.setParameter(): 绑定任意类型的参数,该方法的第三个参数显式指定Hibernate映射类型
- HQL采用ORDER BY关键字对查询结果排序
下面详细介绍下Hibernate的HQL的几个功能:
分页查询:
- setFirstResult(int firstResult): 设定从哪一个对象开始检索,参数firstResult表示这个对象在查询结果中的索引位置,索引位置的起始值为0.默认情况下,Query从查询结果中的第一个对象开始检索
- setMaxResult(int maxResults): 设定一次最多检索出的对象的数目。在默认情况下,Query和Criteria接口检索出查询结果中所有的对象
在映射文件中定义命名查询语句
- Hibernate允许在映射文件中定义字符串形式的查询语句
- <query>元素用于定义一个HQL查询语句,它和<class>元素并列
<query name="salaryEmps"><![CDATA[FROM Employee w WHERE e.salary > :minSal AND e.salary < :maxSal]]></query>
- 在程序中通过Session的getNamedQuery()方法获取查询语句对应的Query对象
投影查询
- 投影查询:查询结果仅包含实体的部分属性。通过SELECT关键字实现
- Query的list()方法返回的集合中包含的是数组类型的元素,每个对象数组代表查询结果的一条记录
- 可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录,使程序代码能完全运用面向对象的语义来访问查询结果集
- 可以通过DISTINCT关键字来保证查询结果不会返回重复元素
报表查询
- 报表查询用于对数据分组和统计,与SQL一样,HQL利用GROUP BY关键字对数据分组,用HAVING关键字对分组数据设定约束条件
- 在HQL查询语句中可以调用以下聚集函数
- count()
- min()
- max()
- sum()
- avg()
HQL(迫切)左外连接
- 迫切左外连接:
- LEFT JOIN FETCH关键字表示迫切左外连接检索策略
- list()方法返回的集合中存放实体对象的引用,每个Department对象关联的Employee集合都被初始化,存放所有关联的Employee的实体对象
查询结果中可能会包含重复元素,可以通过一个HashSet来过滤重复元素
- 左外连接:
- LEFT JOIN关键字表示左外连接
- list()方法返回的集合中存放的是对象数组类型
- 根据配置文件来决定Employee集合的检索策略
- 如果希望list()方法返回的集合中仅包含Department对象,可以在HQL查询语句中使用SELECT关键字
HQL(迫切)内连接
- 迫切内连接:
- INNER JOIN FETCH关键字表示迫切内连接,也可以省略INNER关键字
- list()方法返回的集合中存放Department对象的引用,每个Department对象的Employee集合都被初始化,存放所有关联的Employee对象
- 内连接
- INNER JOIN关键字表示内连接,也可以省略INNER关键字
- list()方法的集合中存放的每个元素对应查询结果的一条记录,每个元素都是对象数组类型
- 如果希望list()方法的返回的集合仅包含Department对象,可以在HQL查询语句中使用SELECT关键字
关联级别运行时的检索策略:
- 如果在HQL中没有显式指定检索策略,将使用映射文件配置的检索策略
- HQL会忽略映射文件中设置的迫切左外连接检索策略,如果希望HQL采用迫切左外连接策略,就必须在HQL查询语句中显式指定它
- 若在HQL代码中显式指定了检索策略,就会覆盖映射文件中配置的检索策略
==============================代码区========================================
Department.java
package com.yl.hibernate.entities; import java.util.HashSet;
import java.util.Set; public class Department { private Integer id;
private String name; private Set<Employee> emps = new HashSet<Employee>(); 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;
} @Override
public String toString() {
//return "Department [id=" + id + ", name=" + name + "]";
return "Department [id=" + id + "]";
} }
Employee.java
package com.yl.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 + "]";
} public Employee(){} public Employee(String email, float salary, Department dept) {
super();
this.salary = salary;
this.email = email;
this.dept = dept;
} }
Department.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2014-12-1 19:29:32 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
<class name="com.yl.hibernate.entities.Department" table="YL_DEPARTMENT">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<set name="emps" table="YL_EMPLOYEE" inverse="true" lazy="true">
<key>
<column name="DEPT_ID" />
</key>
<one-to-many class="com.yl.hibernate.entities.Employee" />
</set>
</class>
</hibernate-mapping>
Employee.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2014-12-1 19:29:32 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
<class name="com.yl.hibernate.entities.Employee" table="YL_EMPLOYEE">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<property name="salary" type="float">
<column name="SALARY" />
</property>
<property name="email" type="java.lang.String">
<column name="EMAIL" />
</property>
<many-to-one name="dept" class="com.yl.hibernate.entities.Department" fetch="join">
<column name="DEPT_ID" />
</many-to-one>
</class> <query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]></query> </hibernate-mapping>
测试类:
package com.yl.hibernate.test; import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set; import oracle.net.aso.e; import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; import com.yl.hibernate.entities.Department;
import com.yl.hibernate.entities.Employee; public class HibernateTest { private SessionFactory sessionFactory;
private Session session;
private Transaction transaction; @Before
public void init() {
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry =
new ServiceRegistryBuilder().applySettings(configuration.getProperties())
.buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); session = sessionFactory.openSession(); transaction = session.beginTransaction();
}
@After
public void destory() {
transaction.commit(); session.close(); sessionFactory.close();
} @Test
public void testHQL() {
//1.创建 Query 对象
//基于位置参数
String hql = "FROM Employee e WHERE e.salary > ? AND e.email LIKE ? AND e.dept = ? ORDER BY e.salary";
Query query = session.createQuery(hql);
//2.绑定参数
//Query 对象调用setXxx()方法支持方法链的编程风格
Department dept = new Department();
dept.setId(30);
query.setFloat(0, 2000)
.setString(1, "%A%")
.setEntity(2, dept);
//3.执行查询
List<Employee> emps = query.list();
System.out.println(emps);
} @Test
public void testHQLNamedParameter() {
//1.创建 Query 对象
//基于命名参数
String hql = "FROM Employee e WHERE e.salary > :sal AND e.email LIKE :email";
Query query = session.createQuery(hql);
//2.绑定参数
query.setFloat("sal", 2000)
.setString("email", "%A%");
//3.执行查询
List<Employee> emps = query.list();
System.out.println(emps.size());
}
/**
* 分页查询
*/
@Test
public void testPageQuery() {
String hql = "FROM Employee";
Query query = session.createQuery(hql); int pageNo = 3;
int pageSize = 5; List<Employee> employees = query.setFirstResult((pageNo-1) * pageSize)
.setMaxResults(pageSize)
.list();
System.out.println(employees); }
/**
* 在映射文件中定义命名查询语句
*/
@Test
public void testNamedQuery() {
Query query = session.getNamedQuery("salaryEmps"); List<Employee> emps = query.setFloat("minSal", 2000)
.setFloat("maxSal", 3000)
.list();
System.out.println(emps); }
/**
* 投影查询
*/
@Test
public void testFieldQuery() {
String hql = "SELECT e.email, e.salary, e.dept FROM Employee e WHERE e.dept = :dept";
Query query = session.createQuery(hql); Department dept = new Department();
dept.setId(20);
List<Object[]> result = query.setEntity("dept", dept).list(); for (Object[] objects : result) {
System.out.println(Arrays.asList(objects));
} } /**
* 投影查询
*/
@Test
public void testFieldQuery2() {
String hql = "SELECT new Employee(e.email, e.salary, e.dept) "
+ "FROM Employee e "
+ "WHERE e.dept = :dept"; Query query = session.createQuery(hql); Department dept = new Department();
dept.setId(20);
List<Employee> result = query.setEntity("dept", dept).list(); for (Employee emp : result) {
System.out.println(emp.getId() + ", " + emp.getEmail() + ", " + emp.getSalary() + ", " + emp.getDept());
} } @Test
public void testGroupBy() {
String hql = "SELECT min(e.salary), max(e.salary) "
+ "FROM Employee e "
+ "GROUP BY e.dept "
+ "HAVING min(salary) > :minSal";
Query query = session.createQuery(hql)
.setFloat("minSal", 700); List<Object[]> result = query.list();
for (Object[] objects : result) {
System.out.println(Arrays.asList(objects));
}
}
/**
* 迫切左外连接
*/
@Test
public void testLeftJoinFetch() {
/*String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps";
Query query = session.createQuery(hql); List<Department> depts = query.list();
System.out.println(depts.size());*/ String hql = "FROM Department d LEFT JOIN FETCH d.emps";
Query query = session.createQuery(hql); List<Department> depts = query.list();
depts = new ArrayList<Department>(new LinkedHashSet<Department>(depts)); System.out.println(depts.size()); for (Department department : depts) {
System.out.println(department.getName() + "-" + department.getEmps().size());
}
} @Test
public void testLeftJoin() {
/*String hql = "FROM Department d LEFT JOIN d.emps";
Query query = session.createQuery(hql); List<Object[]> result = query.list();
System.out.println(result); for (Object[] objects : result) {
System.out.println(Arrays.asList(objects));
}*/ String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN d.emps";
Query query = session.createQuery(hql); List<Department> depts = query.list();
System.out.println(depts.size()); for (Department department : depts) {
System.out.println(department.getName() + ", " + department.getEmps().size());
}
} @Test
public void testInnerJoinFetch() {
/*String hql = "SELECT DISTINCT d FROM Department d INNER JOIN FETCH d.emps";
Query query = session.createQuery(hql); List<Department> depts = query.list();
System.out.println(depts.size());*/ String hql = "FROM Department d INNER JOIN FETCH d.emps";
Query query = session.createQuery(hql); List<Department> depts = query.list();
depts = new ArrayList<Department>(new LinkedHashSet<Department>(depts)); System.out.println(depts.size()); for (Department department : depts) {
System.out.println(department.getName() + "-" + department.getEmps().size());
}
} @Test
public void testInnerJoin() {
/*String hql = "FROM Department d INNER JOIN d.emps";
Query query = session.createQuery(hql); List<Object[]> result = query.list();
System.out.println(result); for (Object[] objects : result) {
System.out.println(Arrays.asList(objects));
}*/ String hql = "SELECT DISTINCT d FROM Department d INNER JOIN d.emps";
Query query = session.createQuery(hql); List<Department> depts = query.list();
System.out.println(depts.size()); for (Department department : depts) {
System.out.println(department.getName() + ", " + department.getEmps().size());
}
} }