一、HQL 检索方式
以双向的一对多来测试 HQL 检索方式。以 Department 和 Employee 为例。
建表语句:
CREATE TABLE department ( dept_id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT, dept_name VARCHAR(50) ); CREATE INDEX FK_8hf3vewo7w3v9doungcc51wwy ON department (dept_id);
CREATE TABLE employee ( emp_id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT, emp_name VARCHAR(50), salary FLOAT, dept_id_fk INT(11), CONSTRAINT FK_miun1wlqp4ujpsgfshyfi7g9j FOREIGN KEY (dept_id_fk) REFERENCES department (dept_id) ); CREATE INDEX FK_miun1wlqp4ujpsgfshyfi7g9j ON employee (dept_id_fk);
对应的实体和 hbm 文件
public class Department { private Integer deptId; private String deptName; private Set<Employee> emps = new HashSet<>(); }
public class Employee { private Integer empId; private String empName; private Float salary; private Department dept; }
<hibernate-mapping> <class name="com.solverpeng.hql.Department" table="department" schema="hibernate"> <id name="deptId" column="dept_id"> <generator class="native"/> </id> <property name="deptName" column="dept_name"/> <set name="emps" inverse="true"> <key> <column name="dept_id_fk"/> </key> <one-to-many not-found="ignore" class="com.solverpeng.hql.Employee"/> </set> </class> </hibernate-mapping>
Department.hbm.xml
<hibernate-mapping> <class name="com.solverpeng.hql.Employee" table="employee" schema="hibernate"> <id name="empId" column="emp_id"> <generator class="native"/> </id> <property name="empName" column="emp_name"/> <property name="salary" column="salary"/> <many-to-one name="dept" class="com.solverpeng.hql.Department"> <column name="dept_id_fk"/> </many-to-one> </class> <query name="findAllEmployees"> <![CDATA[ from Employee ]]> </query> </hibernate-mapping>
Employee.hbm.xml
1.在查询语句中设定各种查询条件
@Test public void testHql(){ Department dept = new Department(); dept.setDeptId(7); List<Employee> list = session.createQuery("FROM Employee e where e.empName like ? and e.empId > ? and dept = ? order by e.empId " + "desc ") .setString(0, "%b%").setInteger(1, 3).setEntity(2, dept).list(); for(Employee employee : list) { System.out.println(employee); } }
说明:
(1)通过 Session 的 createQuery(hql) 方法创建一个 Query 对象,hql 支持动态绑定参数。调用 Query 的相关方法执行查询。
(2)Query 接口支持链式操作,它的 setXxx() 方法返回自身实例。
(3)方法 setEntity(obj),obj 只需要绑定一个 id 就可以。
(4)支持 order by 排序。
(5)参数的位置从 0 开始。
@Test public void testHqlNamed() { List<Employee> list = session.createQuery("from Employee e where e.empName like :name and e.empId > :id and e.dept = ?") .setString("name", "%a%").setInteger("id", 1).list(); for(Employee employee : list) { System.out.println(employee); } }
说明:
(1)支持按照参数名字查询,定义的参数名以 ":" 开头。
2.查询对象的部分属性(查询结果仅包含实体的部分属性)
@Test public void testPropertyQuery() { Department dept = new Department(); dept.setDeptId(7); List<Object[]> list = session.createQuery("select empName, empId from Employee where dept = ?").setEntity(0, dept).list(); for(Object[] objects : list) { System.out.println(Arrays.asList(objects)); } }
说明:
(1)这种情况下查询出来的是一个 Object[] 数组类型。
@Test public void testPropertyQuery2() { Department dept = new Department(); dept.setDeptId(7); List<Employee> list = session.createQuery("select new Employee (empId, empName) from Employee where dept = ?").setEntity(0, dept) .list(); for(Employee employee : list) { System.out.println(employee); } }
(1)查询出来的是 Employee 类型
(2)需要在 Employee 实体类中定义相应的构造器,注意顺序。同时添加一个无参的构造器。
(3)可以通过 Distinct 关键字来去重。
3.分页查询
@Test public void testHqlPage() { int pageNo = 2; int pageSize = 3; List<Employee> list = session.createQuery("from Employee").setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list(); for(Employee employee : list) { System.out.println(employee); } }
(1)setFirstResult(int firstResult):设定从哪一个对象开始检索。
(2)setMaxResults(int maxResult) : 设定每次检索多少条记录。
4.命名查询(很少用到)
@Test public void testNamedQuery() { int pageNo = 3; int pageSize = 5; List<Employee> employees = session.getNamedQuery("findAllEmployees").setFirstResult((pageNo - 1) * pageSize).setMaxResults( pageSize).list(); for(Employee employee : employees) { System.out.println(employee); } }
说明:
(1)其中 findAllEmployees 定义在了 Employee.hbm.xml 文件中,用 <query>元素来定义,和 class 节点并列。
(2)通过 Session 的 getNamedQuery() 方法获取对应的 Query 对象。
5.聚集函数和分组查询
@Test public void testFunction() { List<Object[]> salary = session.createQuery("select dept.deptName, min(salary), max(salary) from Employee group by dept HAVING min(salary) > :salary") .setFloat("salary", 4000).list(); for(Object[] objects : salary) { System.out.println(Arrays.asList(objects)); } }
说明:
(1)通过 GROUP BY 进行分组,通过 HAVING 对分组数据设定约束条件。
(2)可以调用的聚集函数:count() 、min()、max()、sum()、avg()
6.迫切左外链接和左外链接
(1)迫切左外链接
@Test public void testHqlFetch() { List list = session.createQuery("from Department d left join fetch d.emps").list(); }
打印 SQL:
Hibernate: select department0_.dept_id as dept1_0_0_, emps1_.emp_id as emp1_1_1_, department0_.dept_name as dept2_0_0_, emps1_.emp_name as emp2_1_1_, emps1_.salary as salary3_1_1_, emps1_.dept_id_fk as dept4_1_1_, emps1_.dept_id_fk as dept4_0_0__, emps1_.emp_id as emp1_1_0__ from hibernate.department department0_ left outer join hibernate.employee emps1_ on department0_.dept_id=emps1_.dept_id_fk
说明:
- 同时查询了 Employee 对象
- list() 方法返回的集合存放的实体对象的引用,每个 Department 关联的 Employee 集合都被初始化。
- 可以通过 distinct 关键字去重,也可以通过一个 HashSet() 去重(new ArrayList<>(new LinkedHashSet(depts)))。
- 此种情况下,会忽略配置文件中检索策略。
(2)左外链接
@Test public void testHqlLeftJoin2() { List<Object[]> list = session.createQuery("from Department d left join d.emps").list(); for(Object[] objects : list) { System.out.println(Arrays.asList(objects)); } }
Hibernate: select department0_.dept_id as dept1_0_0_, emps1_.emp_id as emp1_1_1_, department0_.dept_name as dept2_0_0_, emps1_.emp_name as emp2_1_1_, emps1_.salary as salary3_1_1_, emps1_.dept_id_fk as dept4_1_1_ from hibernate.department department0_ left outer join hibernate.employee emps1_ on department0_.dept_id=emps1_.dept_id_fk
说明:
- list() 方法返回的集合中存放的是对象数组类型。
- 根据配置文件来决定 Employee 集合的初始化时机。
7.迫切内连接和内连接
(1)迫切内连接(inner join fetch),与迫切左外链接类似,查询的时候同时将关联的另一端的对象进行了初始化。
(2)内连接(inner join),与左外链接类似,查询的时候是根据配置文件中的检索策略来决定另一端初始化的时机。
8.小结
(1)如果在 HQL 中没有显式的指定检索策略,则使用配置文件中的检索策略。
(2)HQL 会忽略配置文件中设置的迫切左外链接检索策略,若想 HQL 采用迫切左外链接策略,就必须在 HQL 语句中显式的指定它。
二、QBC 检索方式
1.设定各种查询条件
(1)like、gt、排序
@Test public void testQBC() { Criteria criteria = session.createCriteria(Employee.class); criteria.add(Restrictions.like("empName", "a", MatchMode.ANYWHERE)); criteria.add(Restrictions.gt("salary", 1000F)); // 排序 criteria.addOrder(Order.desc("salary")); List list = criteria.list(); System.out.println(list); }
Hibernate: select this_.emp_id as emp1_1_0_, this_.emp_name as emp2_1_0_, this_.salary as salary3_1_0_, this_.dept_id_fk as dept4_1_0_ from hibernate.employee this_ where this_.emp_name like ? and this_.salary>? order by this_.salary desc
(2)and、or
public void testQbc2() { Criteria criteria = session.createCriteria(Employee.class); Conjunction conjunction = Restrictions.conjunction(); conjunction.add(Restrictions.like("empName", "a", MatchMode.ANYWHERE)); Department department = new Department(); department.setDeptId(6); conjunction.add(Restrictions.eq("dept", department)); Disjunction disjunction = Restrictions.disjunction(); disjunction.add(Restrictions.gt("salary", 1000F)); disjunction.add(Restrictions.lt("salary", 20000F)); criteria.add(conjunction).add(disjunction); criteria.list(); }
Hibernate: select this_.emp_id as emp1_1_0_, this_.emp_name as emp2_1_0_, this_.salary as salary3_1_0_, this_.dept_id_fk as dept4_1_0_ from hibernate.employee this_ where ( this_.emp_name like ? and this_.dept_id_fk=? ) and ( this_.salary>? or this_.salary<? )
2.分页查询
@Test public void testQbc4() { Criteria criteria = session.createCriteria(Employee.class); // 分页 int pageNo = 2; int pageSize = 4; List<Employee> list = criteria.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list(); }
Hibernate: select this_.emp_id as emp1_1_0_, this_.emp_name as emp2_1_0_, this_.salary as salary3_1_0_, this_.dept_id_fk as dept4_1_0_ from hibernate.employee this_ limit ?, ?
3.聚集函数查询
@Test public void testQbc3() { Criteria criteria = session.createCriteria(Employee.class); criteria.setProjection(Projections.max("salary")); String maxSalary = criteria.uniqueResult().toString(); System.out.println(maxSalary); }
Hibernate: select max(this_.salary) as y0_ from hibernate.employee this
4.小结
(1)创建 QBC 查询:session.createCriteria()
(2)like 关键字:Restrictions.like(),MatchMode.ANYWHERE
(3)排序:criteria.addOrder(),Order.desc、Order.asc
(4)AND:Conjunction conjunction = Restrictions.conjunction()
(5)Or : Disjunction disjunction = Restrictions.disjunction()
(6)聚集函数:criteria.setProjection(Projections.max("salary"))
(7)查询单个对象:criteria.uniqueResult()
(8)查询对象列表:criteria.list()
三、本地 SQL
@Test public void testNativeSql() { Employee employee = new Employee(); employee.setEmpId(5); String empName = (String) session.createSQLQuery("SELECT emp_name FROM employee where emp_id = ?") .setEntity(0, employee).uniqueResult(); System.out.println(empName); }
Hibernate: SELECT emp_name FROM employee where emp_id = ? bb2
通过 session.createSQLQuery() 方法来创建本地 SQL 查询对象。
四、HQL 的更新操作
@Test public void testHqlUpdate() { session.createQuery("delete from Employee where empId = ?").setInteger(0, 13).executeUpdate(); }
Hibernate: delete from hibernate.employee where emp_id=?
五、总结
介绍了 HQL、QBC、本地SQL查询。查询对象都是通过 Session 来创建的。依次为:session.createQuery()、session.createCriteria()、session.createSQLQuery()。
其中 QBC 提供了比 HQL 更为彻底的,更加面向 Java 编程风格的一种方式。在学习 HQL 的时候,需要重点关注迫切左外链接。本地化查询作为对 HQL 的一种补充。
学习的时候,注意对比学习。
更多内容请参看:documentation/manual/en-US/html_single/index.html