Hibernate提供的检索对象的方式主要有导航对象图检索、OID检索、HQL检索、QBC检索和本地SQL检索共五种方式,具体说明如下:
- 导航对象图检索:根据已经加载的对象导航到其他对象;
- OID检索方式:按照对象的OID来检索对象;
- HQL检索方式:使用面向对象的HQL查询语言;
- QBC检索方式:使用QBC(Query By Criteria) API来检索对象,其封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口;
- 本地SQL检索方式:使用本地数据库的SQL查询语句。
1. HQL检索方式
HQL(Hibernate Query Language)是面向对象的查询语言,与SQL查询语言相似;在Hibernate所提供的各种检索方式中,HQL是使用最广的一种检索方式。
1.1 基本功能
- 在查询语句中设定各种查询条件
- 支持投影查询,即仅检索出对象的部分属性
- 支持分页查询
- 支持连接查询
- 支持分组查询, 允许使用HAVING和GROUP BY关键字
- 提供内置聚集函数,如sum()、min()和max()
- 支持子查询
- 支持动态绑定参数
- 能够调用用户定义的SQL函数或标准的SQL函数
1.2 实现步骤
- 调用Session对象的createQuery()方法创建Query对象,其需传入一个HQL查询语句(可以包含命名参数);
- 动态绑定参数;
- 调用Query(支持方法链编程风格)对象的相关方法执行查询语句。
1.3 绑定参数
1.4 分页查询
- setFirstResult(int firstResult)方法:设定从哪一个对象开始检索,参数用于指定该对象在查询结果中的索引位置,索引位置的起始值为0;默认情况下,Query接口对象从查询结果中的第一个对象开始检索;
- setMaxResults(int maxResults):设定一次最多检索出的对象数目,默认情况下,Query和Criteria接口检索出查询结果中所有的对象。
1.5 命名查询
- 配置: Hibernate允许在映射文件中使用query元素(与class元素并列)定义HQL查询语句;
- 查询:在程序中通过Session对象的getNamedQuery()方法获取查询语句所对应的Query对象。
1.6 投影查询
基本概念: 投影查询结果中仅包含实体的部分属性,其通过SELECT关键字来实现。
查询结果: Query接口的list()方法返回的集合中包含的是数组类型的元素,每个对象数组代表查询结果的一条记录。
注意事项: 可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录,使程序代码能完全运用面向对象的语义来访问查询结果集;
注意事项: 可以通过DISTINCT关键字来保证查询结果不会返回重复元素。
1.7 报表查询
基本概念: 报表查询用于对数据分组和统计,HQL查询语句利用GROUP BY关键字对数据分组,用HAVING关键字为分组数据设定约束条件。
聚集函数: 在HQL查询语句中可以调用count()、min()、max()、sum()和avg()等聚集函数。
1.8 迫切左外连接
1.9 迫切内连接
1.10 关联级别运行时的检索策略
如果在HQL中没有显式指定检索策略,将使用映射文件配置的检索策略;HQL查询会忽略映射文件中设置的迫切左外连接检索策略,如果希望HQL采用迫切左外连接策略,就必须在HQL查询语句中显式的指定;若在HQL查询语句中显式指定了检索策略,就会覆盖映射文件中配置的检索策略。
2 QBC检索与本地SQL检索方式
QBC查询是通过使用Hibernate提供的Query By Criteria API来查询对象,其封装了SQL语句的动态拼装,为查询提供了更加面向对象的功能接口。
而本地SQL查询主要用来完善HQL所不能涵盖的查询特性,如插入操作。
附录:测试代码
HibernateTest.java:
package com.qiaobc.hibernate.hql;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest {
private Session session = null;
private Transaction transaction = null;
private SessionFactory sessionFactory = null;
@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();
}
// 1. HelloWorld测试
@Test
public void testHQL() {
// 基于位置的参数
// String hql = "FROM Employee e WHERE e.salary > ? AND e.email LIKE ?";
// Query query = session.createQuery(hql);
// query.setFloat(0, 12012).setString(1, "%4%");
// 1. 创建Query对象
// 基于名字的参数
String hql = "FROM Employee e WHERE e.salary > :sal AND e.email LIKE :email AND e.dept = :dept ORDER BY e.salary";
Query query = session.createQuery(hql);
// 2. 绑定参数
Department dept = (Department) session.get(Department.class, 1005);
query.setFloat("sal", 12012).setString("email", "%3%")
.setEntity("dept", dept);
// 3. 开始查询
List<Employee> emps = query.list();
System.out.println(emps);
}
/**
* 2. 分页查询
*/
@Test
public void testPageQuery() {
String hql = "FROM Employee";
Query query = session.createQuery(hql);
int pageNo = 4; // 页码数
int pageSize = 3; // 每页记录数
List<Employee> emps = query.setFirstResult((pageNo - 1) * pageSize)
.setMaxResults(pageSize).list();
System.out.println(emps);
}
/**
* 3. 命名查询
* <query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minsal AND e.salary < :maxsal]]></query>
*/
@Test
public void testNamedQuery() {
Query query = session.getNamedQuery("salaryEmps");
List<Employee> emps = query.setFloat("minsal", 12009).setFloat("maxsal", 12012).list();
System.out.println(emps);
}
/**
* 4. 投影查询
*/
@Test
public void testFieldQuery1() {
String hql = "SELECT e.email, e.salary FROM Employee e WHERE e.dept = :dept";
Query query = session.createQuery(hql);
Department dept = new Department();
dept.setId(1006);
// 查询结果为数组
List<Object[]> result = query.setEntity("dept", dept).list();
for(Object[] objs : result) {
System.out.println(Arrays.asList(objs));
}
}
@Test
public void testFieldQuery2() {
// 查询结果为Employee对象的集合
String hql = "SELECT new Employee(e.salary, e.email) FROM Employee e "
+ "WHERE e.dept = :dept";
Query query = session.createQuery(hql);
Department dept = new Department();
dept.setId(1006);
List<Employee> result = query.setEntity("dept", dept).list();
System.out.println(result);
}
/**
* 5. 报表查询
*/
@Test
public void testGroupBy() {
String hql = "SELECT min(e.salary), max(e.salary)"
+ "FROM Employee e "
+ "GROUP BY e.dept "
+ "HAVING min(e.salary) > :minsal";
Query query = session.createQuery(hql);
List<Object[]> result = query.setFloat("minsal", 12003).list();
for(Object[] objs : result) {
System.out.println(Arrays.asList(objs));
}
}
/**
* 6. 迫切左外连接
*/
@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();
// depts = new ArrayList<>(new LinkedHashSet<>(depts)); // 去重
System.out.println(depts);
}
/**
* QBC检索方式
*/
@Test
public void testQBC() {
// 1. 创建一个Criteria对象
Criteria criteria = session.createCriteria(Employee.class);
// 2. 添加查询条件:在QBC中查询条件使用Criterion来表示
// Criterion可以通过Restrictions的静态方法得到
// criteria.add(Restrictions.eq("email", "qiaobc2012@163.com"));
// criteria.add(Restrictions.eq("salary", 12012F));
// 1). AND : Conjunction本身就是一个Criteria对象,且其中还可以添加Criterion对象
Conjunction conjunction = Restrictions.conjunction();
conjunction.add(Restrictions.like("name", "3", MatchMode.ANYWHERE));
Department dept = new Department();
dept.setId(1003);
conjunction.add(Restrictions.eq("dept", dept));
// 条件1:(name like %3% and dept=Department [id=1003, name=null])
System.out.println(conjunction);
// 2). OR
Disjunction disjunction = Restrictions.disjunction();
disjunction.add(Restrictions.like("name", "3", MatchMode.ANYWHERE));
disjunction.add(Restrictions.eq("dept", dept));
// 条件2:(name like %3% or dept=Department [id=1003, name=null])
System.out.println(disjunction);
criteria.add(conjunction);
criteria.add(disjunction);
criteria.list(); // 查询条件为:条件1 + 条件2
// 3. 执行查询
// Employee employee = (Employee) criteria.uniqueResult();
// System.out.println(employee);
// 统计查询:Projections
// criteria.setProjection(Projections.max("salary"));
// System.out.println(criteria.list());
// 添加排序:Order
// criteria.addOrder(Order.asc("salary"));
// criteria.addOrder(Order.desc("email"));
// 添加翻页:setFirstResult()方法
}
// HQL支持查询和删除修改操作,但不支持插入操作
@Test
public void testNativeSQL() {
String sql = "INSERT INTO departments VALUE(?, ?)";
Query query = session.createSQLQuery(sql);
query.setInteger(0, 1008).setString(1, "qiaobei").executeUpdate();
}
@After
public void destory() {
transaction.commit();
session.close();
sessionFactory.close();
}
}
Department.java:
package com.qiaobc.hibernate.hql;
import java.util.HashSet;
import java.util.Set;
public class Department {
private Integer id;
private String name;
private Set<Employee> emps = new HashSet<>();
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 + "]";
}
}
Employee.java:
package com.qiaobc.hibernate.hql;
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;
}
public Employee(float salary, String email) {
super();
this.salary = salary;
this.email = email;
}
public Employee() {
super();
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", salary=" + salary
+ ", email=" + email + ", 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 2017-2-24 20:06:22 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="com.qiaobc.hibernate.hql">
<class name="Department" table="DEPARTMENTS">
<id name="id" type="java.lang.Integer">
<column name="DEPT_ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="DEPT_NAME" />
</property>
<set name="emps" table="EMPLOYEES" inverse="true">
<!-- 当前表在EMPLOYEES表中的外键 -->
<key column="DEPARTMENT_ID"></key>
<one-to-many class="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 2017-2-24 20:06:22 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="com.qiaobc.hibernate.hql">
<class name="Employee" table="EMPLOYEES">
<id name="id" type="java.lang.Integer">
<column name="EMP_ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="EMP_NAME" />
</property>
<property name="salary" type="float">
<column name="EMP_SALARY" />
</property>
<property name="email" type="java.lang.String">
<column name="EMP_EMAIL" />
</property>
<many-to-one name="dept" class="Department" column="DEPARTMENT_ID"></many-to-one>
</class>
<query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minsal AND e.salary < :maxsal]]></query>
</hibernate-mapping>
hibernate.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置连接数据库的基本信息 -->
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql:///hibernate</property>
<!-- 配置Hibernate的基本信息 -->
<!-- 配置Hibernate所使用的数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<!-- 执行操作时是否在控制台打印SQL语句 -->
<property name="show_sql">true</property>
<!-- 执行操作时是否对SQL语句进行格式化 -->
<property name="format_sql">true</property>
<!-- 执行操作时自动生成数据表的具体策略 -->
<property name="hbm2ddl.auto">update</property>
<!-- 1). 配置数据库的事务隔离级别为:READ UNCOMMITED -->
<property name="hibernate.connection.isolation">1</property>
<!-- 2). 配置数据库的事务隔离级别为:READ COMMITED -->
<property name="hibernate.connection.isolation">2</property>
<!-- 3). 配置数据库的事务隔离级别为:REPEATABLE READ -->
<property name="hibernate.connection.isolation">4</property>
<!-- 4). 配置数据库的事务隔离级别为:SERIALIZEABLE -->
<property name="hibernate.connection.isolation">8</property>
<!-- 删除对象后使其OID为null -->
<property name="hibernate.use_identifier_rollback">true</property>
<!-- 指定所关联的对象关系映射文件 -->
<mapping resource="com/qiaobc/hibernate/hql/Employee.hbm.xml"/>
<mapping resource="com/qiaobc/hibernate/hql/Department.hbm.xml"/>
</session-factory>
</hibernate-configuration>