一、HQL查询
以双向一对多的 Department--Employee 为例
1.基本步骤:
(1)创建query对象
(2)绑定参数
(3)执行查询
HQL语句中的参数可基于位置,也可基于命名
基于位置的参数(占位符使用?)
//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());基于命名的参数(占位符使用:参数名)
//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());注意:
1.HQL中的参数可以是实体类类型,使用 setEntity()设置。
2.可以写 ORDER BY 进行排序
2.HQL分页查询
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);通过 Query 的 setFirstResult,setMaxResults方法可以进行分页查询
3.HQL命名查询
命名查询可以把HQL放到配置文件中,方便维护
例:查询月薪在一定范围内的 Employee
(1)employee.hbm.xml中定义命名查询 salaryEmps
<hibernate-mapping> <class name="com.atguigu.hibernate.entities.Employee" table="GG_EMPLOYEE"> ...... </class> <query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]></query> </hibernate-mapping>
(2)java中使用该命名查询
Query query = session.getNamedQuery("salaryEmps"); List<Employee> emps = query.setFloat("minSal", 5000) .setFloat("maxSal", 10000) .list(); System.out.println(emps.size());
4.Hibernate 投影查询(只查部分字段)
例:查询员工的 email,工资,部门 三个字段
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)); }注意:
默认情况下返回List<Object[]>
可优化方案:
在Employee中定义一个带有 email,工资,部门 三个字段的构造方法,使投影查询返回List<Employee>
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()); }
5.HQL中使用 函数,GROUP BY,HAVING
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)); }
6.HQL多表连接
6.1迫切左外连接 LEFT JOIN FETCH
// String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps"; //去重方法1:增加 SELECT DISTINCT String hql = "FROM Department d INNER JOIN FETCH d.emps"; Query query = session.createQuery(hql); List<Department> depts = query.list(); //去重方法2:使用LinkedHashSet包装,再用ArrayList包装回来 depts = new ArrayList<>(new LinkedHashSet(depts)); System.out.println(depts.size()); for(Department dept: depts){ System.out.println(dept.getName() + "-" + dept.getEmps().size()); }注意:1.使用 String hql = "FROM Department d INNER JOIN FETCH d.emps"; 查询后,结果会有重复的部门。需去除重复元素
方法:(1) hql中增加SELECT DISTINCT (2) 先用LinkedHashSet包装,再用ArrayList包装回来
2.list()方法返回的集合中,存放的为实体对象(Department)的引用,每个Department关联的Employee集合都被初始化,存放所有关联Employee的实体对象
6.2 左外连接 LEFT JOIN
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 [] objs: result){ System.out.println(Arrays.asList(objs)); }
注意:
1.list()方法返回的集合中,存放的是Object[] 类型
2.根据配置文件决定Employee集合的检索策略
3.如果希望list()方法返回的集合中仅包含Department对象,可以在HQL中使用 SELECT 关键字
6.3迫切内连接
INNER JOIN FETCH 内连接 INNER JOIN
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());
与左外连接区别:不返回左表不满足条件的记录(此示例中 dept_id 为空的不返回)
7.HQL关联级别时的运行检索策略
(1)如果HQL中没有显式指定检索策略,将使用映射文件配置的检索策略
(2)HQL会忽略映射文件中配置的迫切左外连接策略,如果希望HQL使用迫切左外连接策略,必须在HQL查询语句中显式指定它
(3)若在HQL中显式指定了检索策略,就会覆盖映射文件中配置的检索策略
二、QBC查询
Query By Criteria API
1.基本步骤
//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);
2.使用 AND OR拼接多个查询条件
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();
注意:拼接出来的AND和OR之间实际上为一个AND关系
3.统计查询
Criteria criteria = session.createCriteria(Employee.class); //统计查询: 使用 Projection 来表示: 可以由 Projections 的静态方法得到 criteria.setProjection(Projections.max("salary")); System.out.println(criteria.uniqueResult());
4.排序、分页查询
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();
三、本地SQL查询
用于完善HQL不能涵盖的查询特性
String sql = "INSERT INTO gg_department VALUES(?, ?)"; Query query = session.createSQLQuery(sql); query.setInteger(0, 280) .setString(1, "ATGUIGU") .executeUpdate();