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
1 package com.yl.hibernate.entities; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class Department { 7 8 private Integer id; 9 private String name;10 11 private Set<Employee> emps = new HashSet<Employee>();12 13 public Integer getId() {14 return id;15 }16 17 public void setId(Integer id) {18 this.id = id;19 }20 21 public String getName() {22 return name;23 }24 25 public void setName(String name) {26 this.name = name;27 }28 29 public Set<Employee> getEmps() {30 return emps;31 }32 33 public void setEmps(Set<Employee> emps) {34 this.emps = emps;35 }36 37 @Override38 public String toString() {39 //return "Department [id=" + id + ", name=" + name + "]";40 return "Department [id=" + id + "]";41 }42 43 }
Employee.java
1 package com.yl.hibernate.entities; 2 3 public class Employee { 4 5 private Integer id; 6 private String name; 7 private float salary; 8 private String email; 9 10 private Department dept;11 12 public Integer getId() {13 return id;14 }15 16 public void setId(Integer id) {17 this.id = id;18 }19 20 public String getName() {21 return name;22 }23 24 public void setName(String name) {25 this.name = name;26 }27 28 public float getSalary() {29 return salary;30 }31 32 public void setSalary(float salary) {33 this.salary = salary;34 }35 36 public String getEmail() {37 return email;38 }39 40 public void setEmail(String email) {41 this.email = email;42 }43 44 public Department getDept() {45 return dept;46 }47 48 public void setDept(Department dept) {49 this.dept = dept;50 }51 52 @Override53 public String toString() {54 return "Employee [id=" + id + "]";55 }56 57 public Employee(){}58 59 public Employee(String email, float salary, Department dept) {60 super();61 this.salary = salary;62 this.email = email;63 this.dept = dept;64 }65 66 }
Department.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 4 <!-- Generated 2014-12-1 19:29:32 by Hibernate Tools 3.4.0.CR1 --> 5 <hibernate-mapping> 6 <class name="com.yl.hibernate.entities.Department" table="YL_DEPARTMENT"> 7 <id name="id" type="java.lang.Integer"> 8 <column name="ID" /> 9 <generator class="native" />10 </id>11 <property name="name" type="java.lang.String">12 <column name="NAME" />13 </property>14 <set name="emps" table="YL_EMPLOYEE" inverse="true" lazy="true">15 <key>16 <column name="DEPT_ID" />17 </key>18 <one-to-many class="com.yl.hibernate.entities.Employee" />19 </set>20 </class>21 </hibernate-mapping>
Employee.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 4 <!-- Generated 2014-12-1 19:29:32 by Hibernate Tools 3.4.0.CR1 --> 5 <hibernate-mapping> 6 <class name="com.yl.hibernate.entities.Employee" table="YL_EMPLOYEE"> 7 <id name="id" type="java.lang.Integer"> 8 <column name="ID" /> 9 <generator class="native" />10 </id>11 <property name="name" type="java.lang.String">12 <column name="NAME" />13 </property>14 <property name="salary" type="float">15 <column name="SALARY" />16 </property>17 <property name="email" type="java.lang.String">18 <column name="EMAIL" />19 </property>20 <many-to-one name="dept" class="com.yl.hibernate.entities.Department" fetch="join">21 <column name="DEPT_ID" />22 </many-to-one>23 </class>24 25 <query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]></query>26 27 </hibernate-mapping>
测试类:
1 package com.yl.hibernate.test; 2 3 4 import java.util.ArrayList; 5 import java.util.Arrays; 6 import java.util.LinkedHashSet; 7 import java.util.List; 8 import java.util.Set; 9 10 import oracle.net.aso.e; 11 12 import org.hibernate.Query; 13 import org.hibernate.Session; 14 import org.hibernate.SessionFactory; 15 import org.hibernate.Transaction; 16 import org.hibernate.cfg.Configuration; 17 import org.hibernate.service.ServiceRegistry; 18 import org.hibernate.service.ServiceRegistryBuilder; 19 import org.junit.After; 20 import org.junit.Before; 21 import org.junit.Test; 22 23 import com.yl.hibernate.entities.Department; 24 import com.yl.hibernate.entities.Employee; 25 26 public class HibernateTest { 27 28 private SessionFactory sessionFactory; 29 private Session session; 30 private Transaction transaction; 31 32 @Before 33 public void init() { 34 Configuration configuration = new Configuration().configure(); 35 ServiceRegistry serviceRegistry = 36 new ServiceRegistryBuilder().applySettings(configuration.getProperties()) 37 .buildServiceRegistry(); 38 39 sessionFactory = configuration.buildSessionFactory(serviceRegistry); 40 41 session = sessionFactory.openSession(); 42 43 transaction = session.beginTransaction(); 44 } 45 @After 46 public void destory() { 47 transaction.commit(); 48 49 session.close(); 50 51 sessionFactory.close(); 52 } 53 54 @Test 55 public void testHQL() { 56 //1.创建 Query 对象 57 //基于位置参数 58 String hql = "FROM Employee e WHERE e.salary > ? AND e.email LIKE ? AND e.dept = ? ORDER BY e.salary"; 59 Query query = session.createQuery(hql); 60 //2.绑定参数 61 //Query 对象调用setXxx()方法支持方法链的编程风格 62 Department dept = new Department(); 63 dept.setId(30); 64 query.setFloat(0, 2000) 65 .setString(1, "%A%") 66 .setEntity(2, dept); 67 //3.执行查询 68 List<Employee> emps = query.list(); 69 System.out.println(emps); 70 } 71 72 @Test 73 public void testHQLNamedParameter() { 74 //1.创建 Query 对象 75 //基于命名参数 76 String hql = "FROM Employee e WHERE e.salary > :sal AND e.email LIKE :email"; 77 Query query = session.createQuery(hql); 78 //2.绑定参数 79 query.setFloat("sal", 2000) 80 .setString("email", "%A%"); 81 //3.执行查询 82 List<Employee> emps = query.list(); 83 System.out.println(emps.size()); 84 } 85 /** 86 * 分页查询 87 */ 88 @Test 89 public void testPageQuery() { 90 String hql = "FROM Employee"; 91 Query query = session.createQuery(hql); 92 93 int pageNo = 3; 94 int pageSize = 5; 95 96 List<Employee> employees = query.setFirstResult((pageNo-1) * pageSize) 97 .setMaxResults(pageSize) 98 .list(); 99 System.out.println(employees);100 101 }102 /**103 * 在映射文件中定义命名查询语句104 */105 @Test106 public void testNamedQuery() {107 Query query = session.getNamedQuery("salaryEmps");108 109 List<Employee> emps = query.setFloat("minSal", 2000)110 .setFloat("maxSal", 3000)111 .list();112 System.out.println(emps);113 114 }115 /**116 * 投影查询117 */118 @Test119 public void testFieldQuery() {120 String hql = "SELECT e.email, e.salary, e.dept FROM Employee e WHERE e.dept = :dept";121 Query query = session.createQuery(hql);122 123 Department dept = new Department();124 dept.setId(20);125 List<Object[]> result = query.setEntity("dept", dept).list();126 127 for (Object[] objects : result) {128 System.out.println(Arrays.asList(objects));129 }130 131 }132 133 /**134 * 投影查询135 */136 @Test137 public void testFieldQuery2() {138 String hql = "SELECT new Employee(e.email, e.salary, e.dept) "139 + "FROM Employee e "140 + "WHERE e.dept = :dept";141 142 Query query = session.createQuery(hql);143 144 Department dept = new Department();145 dept.setId(20);146 List<Employee> result = query.setEntity("dept", dept).list();147 148 for (Employee emp : result) {149 System.out.println(emp.getId() + ", " + emp.getEmail() + ", " + emp.getSalary() + ", " + emp.getDept());150 }151 152 }153 154 @Test155 public void testGroupBy() {156 String hql = "SELECT min(e.salary), max(e.salary) " 157 + "FROM Employee e "158 + "GROUP BY e.dept "159 + "HAVING min(salary) > :minSal";160 Query query = session.createQuery(hql)161 .setFloat("minSal", 700);162 163 List<Object[]> result = query.list();164 for (Object[] objects : result) {165 System.out.println(Arrays.asList(objects));166 }167 }168 /**169 * 迫切左外连接170 */171 @Test172 public void testLeftJoinFetch() {173 /*String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps";174 Query query = session.createQuery(hql);175 176 List<Department> depts = query.list();177 System.out.println(depts.size());*/178 179 String hql = "FROM Department d LEFT JOIN FETCH d.emps";180 Query query = session.createQuery(hql);181 182 List<Department> depts = query.list();183 depts = new ArrayList<Department>(new LinkedHashSet<Department>(depts));184 185 System.out.println(depts.size());186 187 for (Department department : depts) {188 System.out.println(department.getName() + "-" + department.getEmps().size());189 }190 }191 192 @Test193 public void testLeftJoin() {194 /*String hql = "FROM Department d LEFT JOIN d.emps";195 Query query = session.createQuery(hql);196 197 List<Object[]> result = query.list();198 System.out.println(result);199 200 for (Object[] objects : result) {201 System.out.println(Arrays.asList(objects));202 }*/203 204 String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN d.emps";205 Query query = session.createQuery(hql);206 207 List<Department> depts = query.list();208 System.out.println(depts.size());209 210 for (Department department : depts) {211 System.out.println(department.getName() + ", " + department.getEmps().size());212 }213 }214 215 @Test216 public void testInnerJoinFetch() {217 /*String hql = "SELECT DISTINCT d FROM Department d INNER JOIN FETCH d.emps";218 Query query = session.createQuery(hql);219 220 List<Department> depts = query.list();221 System.out.println(depts.size());*/222 223 String hql = "FROM Department d INNER JOIN FETCH d.emps";224 Query query = session.createQuery(hql);225 226 List<Department> depts = query.list();227 depts = new ArrayList<Department>(new LinkedHashSet<Department>(depts));228 229 System.out.println(depts.size());230 231 for (Department department : depts) {232 System.out.println(department.getName() + "-" + department.getEmps().size());233 }234 }235 236 @Test237 public void testInnerJoin() {238 /*String hql = "FROM Department d INNER JOIN d.emps";239 Query query = session.createQuery(hql);240 241 List<Object[]> result = query.list();242 System.out.println(result);243 244 for (Object[] objects : result) {245 System.out.println(Arrays.asList(objects));246 }*/247 248 String hql = "SELECT DISTINCT d FROM Department d INNER JOIN d.emps";249 Query query = session.createQuery(hql);250 251 List<Department> depts = query.list();252 System.out.println(depts.size());253 254 for (Department department : depts) {255 System.out.println(department.getName() + ", " + department.getEmps().size());256 }257 }258 259 }