zoukankan      html  css  js  c++  java
  • Hibernate-02 HQL实用技术

    学习任务

    • Query接口的使用
    • HQL基本用法
    • 动态参数绑定查询

     

    HQL的使用

    Hibernate支持三种查询方式:HQL查询、Criateria查询、Native SQL查询。

    HQL是Hibernate查询语言(Hibernate Query Language),是一种面向对象的查询语言, 其中没有表和字段的概念,只有类、对象和属性的概念。

    HQL语句编写

    HQL语句除了Java类和属性的名称外,对大小写不敏感。在实际使用中通常对HQL中的关键字使用小写字母。

    1.from

    # 查询所有部门
    from  com.etc.entity.Dept --部门全限定类名
    from  Dept  --省略了包名
    from  Dept as dept --使用as指定Dept的别名为dept
    from  Dept dept --指定别名as可选
    

      

     2.select

    select子句用于选择对象和属性。

    # 查询部门名称
    select dept.deptName from Dept as dept
    # 使用部门别名
    select dept from Dept as dept
    

      

    3.where

    where字句用于构造查询条件。

    # 查询部门名称是SALES的部门对象
    from  Dept  where  deptName = 'SALES' 
    # 使用指派的别名查询
    from  Dept as dept  where  dept.deptName = 'SALES'
    # 查询部门地址不为NULL的部门对象 
    from  Dept  dept  where  dept.location  is not null
    

      

    4.表达式的使用

    表达式一般用在where子句中。以下两个HQL语句在where子句中分别使用了lower()函数和year()函数。

    # 查询部门名称是sales的部门对象,不区分大小写
    from Dept dept where lower(dept.deptName) = 'sales'
    # 查询2016年入职的员工
    from Emp where year(hireDate) = 2016
    

      

    5.Order by

    用于按照指定属性排序。

    # 查询所有员工,按照入职时间升序排序
    from Emp order by hireDate asc
    # 查询所有员工,按照入职时间升序排序。如果入职时间相同,则按照工资降序排序。
    from Emp order by hireDate, salary desc
    

      

    执行HQL语句

    步骤

    1. 获取Session对象
    2. 编写HQL语句
    3. 创建Query对象
    4. 执行查询,得到查询结果

    list()方法执行HQL

    1.DAO关键代码

    session = sessionFactory.getCurrentSession();
    String hql = "from Emp";
    Query query = session.createQuery( hql );
    List<Emp> empList = query.list();
    

      

    2.BIZ关键代码

    	/** list方法查询全部信息 */
    	public List<Emp> listEmps() {
    		Transaction tx = null;
    		List<Emp> list = null;
    		try {
    			tx = empDao.getSession().beginTransaction();
    			list = empDao.listEmp();
    			tx.commit();
    		} catch (Exception e) {
    			e.printStackTrace();
    			if (tx != null) {
    				tx.rollback();
    			}
    		}
    		return list;
    	}
    

      

    3.测试方法关键代码

    	public void testList() {
    		List<Emp> list = new EmpBiz().listEmps();
    		System.out.println("
    ===员工信息集合===");
    		for (Emp item : list) {
    			System.out.println(item.getEmpNo() + "	" + item.getEmpName());
    		}
    
    	}
    

      

    4.Hibernate生成SQL语句

    Hibernate: 
        select
            emp0_."EMPNO" as EMPNO1_1_,
            emp0_."ENAME" as ENAME2_1_,
            emp0_."JOB" as JOB3_1_,
            emp0_."MGR" as MGR4_1_,
            emp0_."HIREDATE" as HIREDATE5_1_,
            emp0_."SAL" as SAL6_1_,
            emp0_."COMM" as COMM7_1_,
            emp0_."DEPTNO" as DEPTNO8_1_ 
        from
            scott."EMP" emp0_
    

      

    iterate()方法执行HQL

    1.DAO关键代码

    session = sessionFactory.getCurrentSession();
    String hql = "from Emp";
    Query query = session.createQuery( hql );
    Iterator<Emp> empIterator = query.iterate();
    

      

    2.BIZ关键代码

    /** iterator方法查询全部信息 */
    	public Iterator<Emp> iteratorEmps() {
    		Transaction tx = null;
    		Iterator<Emp> it = null;
    		try {
    			tx = empDao.getSession().beginTransaction();
    			// 遍历输出结果,与list方法不同的地方在于需要在一个会话结束前输出查询结果
    			it = empDao.iteratorEmp();
    			Emp item = null;
    			System.out.println("
    ===员工信息集合===");
    			while (it.hasNext()) {
    				item = it.next();
    				System.out.println(item.getEmpNo() + "	" + item.getEmpName());
    			}
    			tx.commit();
    		} catch (Exception e) {
    			e.printStackTrace();
    			if (tx != null) {
    				tx.rollback();
    			}
    		}
    		return it;
    	}
    

      

    3.测试方法关键代码

    	public void testIterator() {
    		new EmpBiz().iteratorEmps();
    	}
    

      

    4.Hibernate生成SQL语句

    先查询全部员工OID信息集合

    Hibernate: 
        select
            emp0_."EMPNO" as col_0_0_ 
        from
            scott."EMP" emp0_
    

      

    需要获取员工其他信息的时候再根据OID对数据库二次查询

    # 执行多条如下SQL语句
    Hibernate: 
        select
            emp0_."EMPNO" as EMPNO1_1_0_,
            emp0_."ENAME" as ENAME2_1_0_,
            emp0_."JOB" as JOB3_1_0_,
            emp0_."MGR" as MGR4_1_0_,
            emp0_."HIREDATE" as HIREDATE5_1_0_,
            emp0_."SAL" as SAL6_1_0_,
            emp0_."COMM" as COMM7_1_0_,
            emp0_."DEPTNO" as DEPTNO8_1_0_ 
        from
            scott."EMP" emp0_ 
        where
            emp0_."EMPNO"=?
    

      

    list()方法和iterator()方法的区别

    • 从Hibernate生成的SQL语句可以看出,list()方法生成一条SQL查询语句,查询所有符合条件的记录。
    • iterate()方法首先查询出所有符合条件的主键值,此处是empno,然后在需要某一对象的其他属性值时,オ生成按主键查询的SQL语句。即iterate()方法可能生成1+N条SQL语句(N为第一条语句获取的EMP对象的数量)。

    HQL参数绑定

    使用字符串拼接查询条件存在各种弊端
    例如HQL语句:

    "from User where name = '" + name + "'"
    

      

    存在问题:

    • 从性能方面,Hibernate底层使用JDBC的PreparedStatement对象访问数据库。如果直接将属性值写在语句中,那么每次执行SQL语句, 数据库都会重新编译SQL语句,从而导致性能降低。
    • 从安全角度,这种字符串的拼装造成的漏洞是SQL注入攻击的主要目标。
    • 在实际开发中,不使用字符串拼接的方式来构建HQL语句,而使用参数绑定的方式。

    参数绑定的形式

    参数绑定的两种形式

    • 按参数位置绑定(下标从0开始

        from User where name = ?

    • 按参数名称绑定(可读性好,易维护,推荐使用

        from User where name = :name

    示例代码

    	//查询参数的绑定方式
    	/**按照参数位置绑定参数:根据部门名称查询部门*/
    	public List<Dept> findByDeptName(String deptName){
    		String hql="from Dept as dept where dept.deptName=?";
    		Query query=this.getSession().createQuery(hql);
    		query.setString(0, deptName);//位置从0开始计算
    		return query.list();		
    	}
    	
    	/**按照参数名称绑定参数:根据部门名称查询部门*/
    	public List<Dept> findByDeptName2(String deptName){
    		String hql="from Dept as dept where dept.deptName=:name";
    		Query query=this.getSession().createQuery(hql);
    		query.setString("name", deptName);//位置从0开始计算
    		return query.list();		
    	}
    

      

    绑定各种类型参数
    1.为任意参数类型赋值

    query接口提供了setXXX()重载方法针对具体数据类型进行赋值(XXX表示各种数据类型):

    setXXX(int position, XXX value);//按照位置传参赋值
    setXXX(String name, XXX value);//按照参数名称传参赋值

    Hibernate提供setParameter()用来绑定任意类型参数。

    setParameter( int position,  Object value);//按照位置为参数赋值
    setParameter( String name, Object value);//按照参数名为参数赋值
    

      

    示例代码

    	/** 使用setParameter()绑定各种类型参数 */
    	public List<Emp> findByConditions(Object[] conditions) {
    		// 查询依赖多个条件,且类型各异
    		String hql = "from Emp where job=? and sal>?";
    		Query query = this.getSession().createQuery(hql);
    		if (conditions != null && conditions.length > 0) {
    			for (int i = 0; i < conditions.length; ++i) {
    				query.setParameter(i, conditions[i]);//为占位符赋值
    			}
    		}
    		return query.list();
    	}
    

      

    2.setProperties()方法:绑定命名参数与一个对象的属性值

    setProperties()方法即可根据参数名获取对象中的相关属性值完成赋值。

    示例代码

    /** 使用setProperties()方法绑定命名参数和一个对象属性值 */
    	public List<Emp> findByConditions2(Emp conditions) {
    		// 查询依赖多个条件,且类型各异
    		String hql = "from Emp where job=:job and sal>:sal";
    		Query query = this.getSession().createQuery(hql);
    		// 根据命名参数的名称,从conditions对象中相同名称属性上取值
    		query.setProperties(conditions);
    		return query.list();
    	}
    

      

    3.NULL

    参数绑定对NULL是安全的,生成的SQL语句条件将是“==null”。

    但如果希望查询到NULL值条件数据。使用IS NULL构造查询条件查询。

    实现动态查询

    在査询条件很多的情况下,传递过多的参数很不方便。此时,可以把参数封装在对象中,再使用之前所述的Query接口的setProperties()方法为HQL中的命名参数赋值。setProperties()方法会把对象的属性匹配到命名参数上,需注意命名参数名称要与Java对象的属性匹配。

    需要解决的问题

    查找出符合以下条件的员工信息

    •   职位是店员,如:job = 'CLERK'
    •   工资大于1000元,如:sal > 1000
    •   入职时间在1981年4月1日至1985年9月9日之间

    问题分析

    • 条件个数不确定
    • 最多有三个
    • 需要动态设置查询条件

    解决方案示例代码

    1.查询条件信息封装类

    package com.etc.entity;
    
    import java.util.Date;
    
    /** 封装员工信息查询条件的实体类 */
    public class EmpCondition {
    	private String job;// 职位
    	private Double sal;// 工资
    	private Date hireDateEnd;// 员工入职结束时间
    	private Date hireDateStart;// 员工入职开始时间
    	public String getJob() {
    		return job;
    	}
    	public void setJob(String job) {
    		this.job = job;
    	}
    	public Double getSal() {
    		return sal;
    	}
    	public void setSal(Double sal) {
    		this.sal = sal;
    	}
    	public Date getHireDateEnd() {
    		return hireDateEnd;
    	}
    	public void setHireDateEnd(Date hireDateEnd) {
    		this.hireDateEnd = hireDateEnd;
    	}
    	public Date getHireDateStart() {
    		return hireDateStart;
    	}
    	public void setHireDateStart(Date hireDateStart) {
    		this.hireDateStart = hireDateStart;
    	}
    
    }
    

      

    2.日期类型转换工具类

    public class Tool {
    
    	/**日期指定格式日期字符串转换为日期对象*/
    	public static Date strToDate(String dateStr, String pattern) {
    		try {
    			return new SimpleDateFormat(pattern).parse(dateStr);
    		} catch (Exception e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    }
    

      

    3.DAO关键代码

    	/** 使用setProperties()实现动态查询 */
    	public List<Emp> findByConditions3(String hql, EmpCondition conditions) {
    		Query query = this.getSession().createQuery(hql);
    		// 根据命名参数的名称,从conditions对象中相同名称属性上取值
    		query.setProperties(conditions);
    		return query.list();
    	}
    

      

    4.BIZ关键代码

    	/** 使用setProperties()实现动态查询 */
    	public List<Emp> findEmpsByConditions3(EmpCondition conditions) {
    		Transaction tx = null;
    		List<Emp> emps = null;
    		try {
    			tx = empDao.getSession().beginTransaction();
    			// 动态生成HQL
    			StringBuilder hql = new StringBuilder("from Emp as emp where 1=1");
    			if (conditions.getJob() != null && conditions.getJob().length() > 0) {
    				hql.append(" and emp.job=:job");
    			}
    			if (conditions.getSal() != null && conditions.getSal() != 0) {
    				hql.append(" and emp.sal>:sal");
    			}
    			if (null != conditions.getHireDateStart()) {
    				hql.append(" and emp.hireDate>:hireDateStart");
    			}
    			if (null != conditions.getHireDateEnd()) {
    				hql.append(" and emp.hireDate<:hireDateEnd");
    			}
    			emps = empDao.findByConditions3(hql.toString(), conditions);
    			tx.commit();
    		} catch (HibernateException e) {
    			e.printStackTrace();
    			if (tx != null) {
    				tx.rollback();
    			}
    		}
    		return emps;
    	}
    

      

    唯一结果查询

     query接口的uniqueResult()可以用于查询唯一结果。

    示例代码

    1.DAO关键代码

    /** 使用uniqueResult()方法获得唯一结果 */
    	public Long obtainTheRowCount(Double sal) {
    		String hql = "select count(id) from Emp where sal >= :sal";// count中的id为对象oid,也可以使用emp属性empNo
    		return (Long) this.getSession().createQuery(hql).setDouble("sal", sal)
    				.uniqueResult();
    
    	}
    

      

    2.BIZ关键代码

    	/** 使用uniqueResult()方法获得唯一结果 */
    	public Long countBySal(Double sal) {
    		Transaction tx = null;
    		Long result = null;
    		try {
    			tx = empDao.getSession().beginTransaction();
    			result = empDao.obtainTheRowCount(sal);
    			tx.commit();
    		} catch (HibernateException e) {
    			e.printStackTrace();
    			if (tx != null) {
    				tx.rollback();
    			}
    		}
    		return result;
    	}
    

      

    注意:当查询结果不唯一的情况下,uniqueResult()会报错。

    分页和投影

    分页查询

    使用到的函数

    • uniqueResult() :获取唯一对象。结合count()函数获取记录数。
    • setFirstResult() :设置从第几条开始
    • setMaxResults():设置读取最大记录数

    分页步骤

    1.使用count函数获得总记录数

    2.计算总页数

    int totalpages = (count%pageSize == 0 ) ? (count / pageSize) : (count / pageSize + 1);
    

      

    3.实现分页查询

    int pageIndex = 2;
    int pageSize = 2;
    List<Emp> empList=new EmpBiz().findEmpsByPage(pageIndex, pageSize);
    for(Emp item:empList){
    	System.out.println("员工编号为:"+item.getEmpNo()+"	"+item.getEmpName());
    }
    

      

    示例代码:(员工信息分页查询为例)

    1.DAO关键代码

    	/** 分页查询 */
    	public List<Emp> findByPage(int pageIndex, int pageSize) {
    		return this.getSession().createQuery("from Emp order by id")
    				.setFirstResult((pageIndex - 1) * pageSize)
    				.setMaxResults(pageSize).list();
    	}
    

      

    2.BIZ关键代码

          /** 分页查询 */
    	public List<Emp> findEmpsByPage(int pageIndex, int pageSize) {
    		Transaction tx = null;
    		List<Emp> empList = null;
    		try {
    			tx = empDao.getSession().beginTransaction();
    			empList = empDao.findByPage(pageIndex, pageSize);
    			tx.commit();
    		} catch (HibernateException e) {
    			e.printStackTrace();
    			if (tx != null) {
    				tx.rollback();
    			}
    		}
    		return empList;
    	}
    

      

    投影查询

    HQL投影查询是查询一个持久化类的一个或多个属性值,或者是通过表达式或聚合函数得到的值。
    投影查询需要使用HQL的select子句,查询结果的封装主要分三种情况:

    • 封装成Object对象
    • 封装成Object数组
    • 通过构造方法封装成对象。(对象不是持久化状态,仅用于封装结果)

    若查询结果仅用于展示,不需要保持持久化状态,应尽量使用投影查询以减少开销,提高效率。

    示例代码

         //投影查询
    	/**获得部门名称集合:只查询一个属性*/
    	public List<String> findaAllNames(){
    		String hql="select deptName from Dept";
    		Query query=this.getSession().createQuery(hql);
    		return query.list();
    	}
    	
    	/**查询部门编号和部门名称:通过数组接收多个属性*/
    	public List<Object[]> findDeptList(){
    		String hql="select deptNo,deptName from Dept";
    		Query query=this.getSession().createQuery(hql);
    		return query.list();
    	}
    	
    	/**查询部门编号和部门名称:通过构造函数接收多个属性*/
    	public List<Dept> findDeptList2(){
    		String hql="select new Dept(deptNo,deptName) from Dept";//dept队形只用于封装,非持久化状态
    		Query query=this.getSession().createQuery(hql);
    		return query.list();
    	}
    

      



    本博客文章未经许可,禁止转载和商业用途!

    如有疑问,请联系: 2083967667@qq.com


  • 相关阅读:
    Ubuntu 18.04更换国内源方法
    CTFHub-Web-Web前置技能-PHPINFO
    CTFHub-Web-Web前置技能-目录遍历
    Python-字符串常见操作
    hadoop完全分布式虚拟机多机克隆后网卡配置
    N皇后问题 回溯非递归算法 C++实现2
    N皇后问题 回溯非递归算法 C++实现1
    N皇后问题 回溯递归算法 C++实现2
    N皇后问题 回溯递归算法 C++实现1
    无法更新运行时文件夹共享状态:在客户机操作系统内装载共享文件夹文件系统时出错——解决方案
  • 原文地址:https://www.cnblogs.com/rask/p/8690862.html
Copyright © 2011-2022 走看看