概述
- 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 keyword
- 提供内置聚集函数, 如 sum(), min() 和 max()
- 支持子查询
- 支持动态绑定參数
HQL 检索方式包含下面步骤:
- 通过 Session 的 createQuery() 方法创建一个 Query 对象, 它包括一个 HQL 查询语句. HQL 查询语句中能够包括命名參数
- 动态绑定參数
- 调用 Query 相关方法运行查询语句
Qurey 接口支持方法链编程风格, 它的 setXxx() 方法返回自身实例, 而不是 void 类型
HQL vs SQL:
- HQL 查询语句是面向对象的, Hibernate 负责解析 HQL 查询语句, 然后依据对象-关系映射文件里的映射信息, 把 HQL 查询语句翻译成对应的 SQL 语句. HQL 查询语句中的主体是域模型中的类及类的属性
- SQL 查询语句是与关系数据库绑定在一起的. SQL 查询语句中的主体是数据库表及表的字段.
绑定參数:
- Hibernate 的參数绑定机制依赖于 JDBC API 中的 PreparedStatement 的提前定义 SQL 语句功能.
- HQL 的參数绑定由两种形式:
- 按參数名字绑定: 在 HQL 查询语句中定义命名參数, 命名參数以 “:” 开头.
- 按參数位置绑定: 在 HQL 查询语句中用 “?
” 来定义參数位置
- 按參数名字绑定: 在 HQL 查询语句中定义命名參数, 命名參数以 “:” 开头.
- 相关方法:
- setEntity(): 把參数与一个持久化类绑定
- setParameter(): 绑定随意类型的參数. 该方法的第三个參数显式指定 Hibernate 映射类型
- setEntity(): 把參数与一个持久化类绑定
分页查询:
- setFirstResult(int firstResult): 设定从哪一个对象開始检索, 參数 firstResult 表示这个对象在查询结果中的索引位置, 索引位置的起始值为 0. 默认情况下, Query 从查询结果中的第一个对象開始检索
- setMaxResults(int maxResults): 设定一次最多检索出的对象的数目. 在默认情况下, Query 和 Criteria 接口检索出查询结果中全部的对象
- Hibernate 同意在映射文件里定义字符串形式的查询语句.
- <query> 元素用于定义一个 HQL 查询语句, 它和 <class> 元素并列.
- 在程序中通过 Session 的 getNamedQuery() 方法获取查询语句相应的 Query 对象.
- 投影查询: 查询结果仅包括实体的部分属性.通过 SELECT keyword实现.
-
Query 的list()方法返回的集合中包括的是数组类型的元素,每一个对象数组代表查询结果的一条记录
-
能够在持久化类中定义一个对象的构造器来包装投影查询返回的记录,使程序代码能全然运用面向对象的语义来訪问查询结果集.
-
能够通过 DISTINCT keyword来保证查询结果不会返回反复元素
- 报表查询用于对数据分组和统计, 与 SQL 一样, HQL 利用 GROUP BYkeyword对数据分组, 用 HAVING
keyword对分组数据设定约束条件.
- 在 HQL 查询语句中能够调用下面聚集函数
count()
min()
max()
sum()
avg()
- 迫切左外连接:
- LEFT JOIN FETCH keyword表示迫切左外连接检索策略
- list() 方法返回的集合中存放实体对象的引用, 每一个 Department 对象关联的 Employee 集合都被初始化, 存放全部关联的 Employee 的实体对象.
- 查询结果中可能会包括反复元素, 能够通过一个 HashSet 来过滤反复元素
- LEFT JOIN FETCH keyword表示迫切左外连接检索策略
- 左外连接:
- LEFT JOIN keyword表示左外连接查询.
-
list() 方法返回的集合中存放的是对象数组类型
-
–依据配置文件来决定 Employee集合的检索策略.
-
假设希望 list() 方法返回的集合中仅包括 Department 对象, 能够在HQL 查询语句中使用 SELECT keyword
HQL (迫切)内连接
- 迫切内连接:
- INNER JOIN FETCH keyword表示迫切内连接, 也能够省略 INNER keyword
- list() 方法返回的集合中存放 Department 对象的引用, 每一个 Department 对象的 Employee 集合都被初始化, 存放全部关联的 Employee 对象
- INNER JOIN FETCH keyword表示迫切内连接, 也能够省略 INNER keyword
- 内连接:
- INNER JOIN keyword表示内连接, 也能够省略 INNER keyword
- list() 方法的集合中存放的每一个元素相应查询结果的一条记录, 每一个元素都是对象数组类型
- 假设希望 list() 方法的返回的集合仅包括 Department 对象, 能够在 HQL 查询语句中使用 SELECT keyword
- INNER JOIN keyword表示内连接, 也能够省略 INNER keyword
关联级别执行时的检索策略
- 假设在 HQL 中没有显式指定检索策略, 将使用映射文件配置的检索策略.
- HQL 会忽略映射文件里设置的迫切左外连接检索策略, 假设希望 HQL 採用迫切左外连接策略, 就必须在 HQL 查询语句中显式的指定它
- 若在 HQL 代码中显式指定了检索策略, 就会覆盖映射文件里配置的检索策略
- QBC 查询就是通过使用 Hibernate 提供的 Query By Criteria API 来查询对象,这样的 API 封装了 SQL 语句的动态拼装。对查询提供了更加面向对象的功能接口
- 本地SQL查询来完好HQL不能涵盖全部的查询特性
实例具体解释:
Department.java
package com.atguigu.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<>(); 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 + "]"; } }
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.atguigu.hibernate.entities.Department" table="GG_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="GG_EMPLOYEE" inverse="true" lazy="true"> <key> <column name="DEPT_ID" /> </key> <one-to-many class="com.atguigu.hibernate.entities.Employee" /> </set> </class> </hibernate-mapping>
package com.atguigu.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(String email, float salary, Department dept) { super(); this.salary = salary; this.email = email; this.dept = dept; } public Employee() { // TODO Auto-generated constructor stub } }
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"> <hibernate-mapping> <class name="com.atguigu.hibernate.entities.Employee" table="GG_EMPLOYEE"> <!-- <cache usage="read-write"/> --> <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.atguigu.hibernate.entities.Department"> <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.atguigu.hibernate.test; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; 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.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.jdbc.Work; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.atguigu.hibernate.dao.DepartmentDao; import com.atguigu.hibernate.entities.Department; import com.atguigu.hibernate.entities.Employee; import com.atguigu.hibernate.hibernate.HibernateUtils; 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 destroy(){ transaction.commit(); session.close(); sessionFactory.close(); } @Test public void testBatch(){ session.doWork(new Work() { @Override public void execute(Connection connection) throws SQLException { //通过 JDBC 原生的 API 进行操作, 效率最高, 速度最快! } }); } @Test public void testHQLUpdate(){ String hql = "DELETE FROM Department d WHERE d.id = :id"; session.createQuery(hql).setInteger("id", 280) .executeUpdate(); } @Test public void testNativeSQL(){ String sql = "INSERT INTO gg_department VALUES(?, ?)"; Query query = session.createSQLQuery(sql); query.setInteger(0, 280) .setString(1, "ATGUIGU") .executeUpdate(); } @Test public void testQBC4(){ Criteria criteria = session.createCriteria(Employee.class); //1. 加入排序 criteria.addOrder(Order.asc("salary")); criteria.addOrder(Order.desc("email")); //2. 加入翻页方法 int pageSize = 5; int pageNo = 3; criteria.setFirstResult((pageNo - 1) * pageSize) .setMaxResults(pageSize) .list(); } @Test public void testQBC3(){ Criteria criteria = session.createCriteria(Employee.class); //统计查询: 使用 Projection 来表示: 能够由 Projections 的静态方法得到 criteria.setProjection(Projections.max("salary")); System.out.println(criteria.uniqueResult()); } @Test public void testQBC2(){ Criteria criteria = session.createCriteria(Employee.class); //1. AND: 使用 Conjunction 表示 //Conjunction 本身就是一个 Criterion 对象 //且当中还能够加入 Criterion 对象 Conjunction conjunction = Restrictions.conjunction(); conjunction.add(Restrictions.like("name", "a", MatchMode.ANYWHERE)); Department dept = new Department(); dept.setId(80); conjunction.add(Restrictions.eq("dept", dept)); System.out.println(conjunction); //2. OR Disjunction disjunction = Restrictions.disjunction(); disjunction.add(Restrictions.ge("salary", 6000F)); disjunction.add(Restrictions.isNull("email")); criteria.add(disjunction); criteria.add(conjunction); criteria.list(); } @Test public void testQBC(){ //1. 创建一个 Criteria 对象 Criteria criteria = session.createCriteria(Employee.class); //2. 加入查询条件: 在 QBC 中查询条件使用 Criterion 来表示 //Criterion 能够通过 Restrictions 的静态方法得到 criteria.add(Restrictions.eq("email", "SKUMAR")); criteria.add(Restrictions.gt("salary", 5000F)); //3. 运行查询 Employee employee = (Employee) criteria.uniqueResult(); System.out.println(employee); } @Test public void testLeftJoinFetch2(){ String hql = "SELECT e FROM Employee e INNER JOIN e.dept"; Query query = session.createQuery(hql); List<Employee> emps = query.list(); System.out.println(emps.size()); for(Employee emp: emps){ System.out.println(emp.getName() + ", " + emp.getDept().getName()); } } @Test public void testLeftJoin(){ 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 dept: depts){ System.out.println(dept.getName() + ", " + dept.getEmps().size()); } // List<Object []> result = query.list(); // result = new ArrayList<>(new LinkedHashSet<>(result)); // System.out.println(result); // // for(Object [] objs: result){ // System.out.println(Arrays.asList(objs)); // } } @Test public void testLeftJoinFetch(){ // String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps"; String hql = "FROM Department d INNER JOIN FETCH d.emps"; Query query = session.createQuery(hql); List<Department> depts = query.list(); depts = new ArrayList<>(new LinkedHashSet(depts)); System.out.println(depts.size()); for(Department dept: depts){ System.out.println(dept.getName() + "-" + dept.getEmps().size()); } } @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", 8000); List<Object []> result = query.list(); for(Object [] objs: result){ System.out.println(Arrays.asList(objs)); } } @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(80); 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 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(80); List<Object[]> result = query.setEntity("dept", dept) .list(); for(Object [] objs: result){ System.out.println(Arrays.asList(objs)); } } @Test public void testNamedQuery(){ Query query = session.getNamedQuery("salaryEmps"); List<Employee> emps = query.setFloat("minSal", 5000) .setFloat("maxSal", 10000) .list(); System.out.println(emps.size()); } @Test public void testPageQuery(){ String hql = "FROM Employee"; Query query = session.createQuery(hql); int pageNo = 22; int pageSize = 5; List<Employee> emps = query.setFirstResult((pageNo - 1) * pageSize) .setMaxResults(pageSize) .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", 7000) .setString("email", "%A%"); //3. 运行查询 List<Employee> emps = query.list(); System.out.println(emps.size()); } @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(80); query.setFloat(0, 6000) .setString(1, "%A%") .setEntity(2, dept); //3. 运行查询 List<Employee> emps = query.list(); System.out.println(emps.size()); } }