一、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 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 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 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
......
:minSal AND e.salary < :maxSal]]>
(2)java中使用该命名查询
Query query = session.getNamedQuery("salaryEmps");
List 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 result = query.setEntity("dept", dept)
.list();
for(Object [] objs: result){
System.out.println(Arrays.asList(objs));
}
注意:
默认情况下返回List
可优化方案:
在Employee中定义一个带有 email,工资,部门 三个字段的构造方法,使投影查询返回List
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 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 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 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 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 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();