Hibernate5中HQL检索方式

HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似。在Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。它具有以下功能:

  • 在查询语句中设定各种查询条件。
  • 支持投影查询,即仅检索出对象的部分属性。
  • 支持分页查询。
  • 支持分组查询,允许使用group by和having关键字。
  • 提供内置聚集函数,如sum()、min()和max()。
  • 能够调用用户定义的SQL函数。
  • 支持子查询,即嵌套查询。
  • 支持动态绑定参数。

Session 类的 Qurey 接口支持HQL检索方式,它提供了以上列出的各种查询功能。

注:Qurey 接口支持方法链编程风格,它的set方法都返回自身实例,而不是返回void类型。方法链编程风格能使程序代码更加简洁。

一、HQL 查询的步骤

    写 HQL 语句注意: 表名与列名 对应 持久化类的类名和字段名,严格区分大小写HQL不支持 * 号,一般使用别名 ,

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		//1. 写HQL  
		String hql = "from Activity";  //等价于: String hql = "select a from Activity a";
		//2. 获取Query<>
		Query<Activity> query = session.createQuery(hql);
		//3. 给动态绑定参数赋值
		//4. 获取list(查询)  增删改 使用executeUpdate()
		List<Activity> list = query.list();
		
		for (Activity activity : list) {
			System.out.println(activity.getAname() +"--"+ activity.getMember().size() );
		}
	}

二. HQL 动态绑定参数查询

    Activity 与 Member  多对多

1). 使用 ? 做占位符: Hibernate setParameter() 占位符参数从 0 开始

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		//1. 写HQL
		String hql = "select a from Activity a where a.id<? and a.aname like ?"; 
		//2. 获取Query<>
		Query<Activity> query = session.createQuery(hql);
		//3. 给动态绑定参数赋值  Hibernate 从0开始
		query.setParameter(0, 5);
		query.setParameter(1, "建%");
		//4. 获取list
		List<Activity> list = query.list();
		
		for (Activity activity : list) {
			System.out.println(activity.getAname() +"--"+ activity.getMember().size() );
		}
	}

--------

建军节--2
Hibernate: 
    select
        member0_.activity_id as activity1_1_0_,
        member0_.member_id as member_i2_1_0_,
        member1_.id as id1_2_1_,
        member1_.m_name as m_name2_2_1_ 
    from
        t_activity_member member0_ 
    inner join
        t_member member1_ 
            on member0_.member_id=member1_.id 
    where
        member0_.activity_id=?
建党节--1

2). 使用 :自定义名 做占位符: Hibernate setParameter() 占位符参数 用字符串表示, 推荐使用 可读性强

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		//1. 写HQL
		String hql = "select a from Activity a where a.id<:aid and a.aname like :an"; 
		//2. 获取Query<>
		Query<Activity> query = session.createQuery(hql);
		//3. 给动态绑定参数赋值  
		query.setParameter("aid", 5);
		query.setParameter("an", "建%");
		//4. 获取list
		List<Activity> list = query.list();
		
		for (Activity activity : list) {
			System.out.println(activity.getAname() +"--"+ activity.getMember().size() );
		}
	}

 

3. where 筛选参数可以是基本数据类型外,还可以是一个对象(必须有id,否则报错)

    Teacher 与 Student  一对多

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
				
		//1. 写HQL
		String hql = "select s from Student s where s.teacher = ?"; 
		//2. 获取Query<>
		Query<Student> query = session.createQuery(hql);
		//3. 给动态绑定对象参数赋值  
		Teacher teacher = new Teacher();
		teacher.setId(3);
		
		query.setParameter(0, teacher);
		//4. 获取list
		List<Student> list = query.list();
		
		for (Student Student : list) {
			System.out.println(Student.getSname() +"--"+ Student.getTeacher().getTname());
		}

}


--------

学生3--老师3
学生4--老师3

 

三、HQL 分页查询: mysql 和 orcale 都适合。 

分页查询主要靠两个方法来实现:

setFirstResult(index) : 从index这个索引值开始查询数据库的记录,  index 从 0 开始

setMaxResults(数量) : 表示从起始的索引对应的记录开始, 最多查询出的记录数目

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		int pageNo = 4;
		int pageSize = 5;
		
		String hql = "from Student where id>?";	//可以加条件
		Query<Student> query = session.createQuery(hql);
		query.setParameter(0, 5);
		//使用下 链式语法
		query.setFirstResult((pageNo-1)*pageSize).setMaxResults(pageSize);
		
		List<Student> list = query.list();
		//System.out.println(Arrays.toString(list.toArray()));
		for (Student Student : list) {
			System.out.println(Student.getSname());
		}
	}

 

四、只查询出表中的部分字段

只查询出表中的部分字段, 在 Hibernate 中叫做投影查询:

1. 方法一:多字段(大于1)查询

	@Test
	public void test() {
		String hql = "select s.sname,s.teacher from Student s";
                //返回结果为 Object[]的List集合
		List<Object[]> list = session.createQuery(hql).list();
		for (Object[] objects : list) {
			System.out.println(Arrays.toString(objects));
		}
	}

--------
[学生1, cn.jq.hibernate5.model.Teacher@5b6e8f77]
[学生2, cn.jq.hibernate5.model.Teacher@5b6e8f77]
[学生3, cn.jq.hibernate5.model.Teacher@6b7d1df8]
[学生4, cn.jq.hibernate5.model.Teacher@6b7d1df8]
[学生5, cn.jq.hibernate5.model.Teacher@3044e9c7]
[学生6, cn.jq.hibernate5.model.Teacher@41d7b27f]
。。。

     方法一投影查询返回结果是一个 List<Object[]> 类型,这是因为查询的字段是多个(大于1),所以是 Object[] 数组,如果我们业务层很多方法都是以对象来传递的,Object 类型要强制类型转换,不好!

2.方法二: 对象查询

	@Test
	public void test() {
		String hql = "select new Student(s.sname) from Student s";
		// 返回结果是 对用POJO类的List集合, 注意:必须要有对应参数的构造方法
		List<Student> list = session.createQuery(hql).list();
		for (Student student : list) {
			System.out.println(student);
		}
	}

----其他值为空或默认基本类型值--
Student [id=0, sname=学生1, teacher=null]
Student [id=0, sname=学生2, teacher=null]
Student [id=0, sname=学生3, teacher=null]
Student [id=0, sname=学生4, teacher=null]
。。。

方法二返回结果是一个 List<POJO类> 对象集合类型,(被泛型限定的POJO类),这种方式解决了传递 Object[] 方式,只传递对象了,且也实现了,只查询部分属性。

注意: POJO 类映射对象中必须要有对应参数查询语句的构造方法

 

五. HQL语句中使用聚合函数

重点内容

函数名说明
count()统计选择对象的记录条数
sum()求和
max()求最大值
min()求最小值
avg()计算选择属性的平均值
	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "select sum(s.id),avg(s.id),max(s.id),min(s.id),count(s.id),s.teacher from Student s group by s.teacher.id having  s.teacher.id < ?";
		//返回结果为 Object[]的List集合
		List<Object[]> list = session.createQuery(hql).setParameter(0, 6).list();
		for (Object[] object : list) {
			System.out.println(Arrays.toString(object));
		}
	}

--------
[3, 1.5, 2, 1, 2, Teacher [id=1, tname=老师1]]
[7, 3.5, 4, 3, 2, Teacher [id=3, tname=老师3]]
[18, 6.0, 7, 5, 3, Teacher [id=4, tname=老师4]]

结论: HQL 语言和 SQL 语言基本上是相通的,只是 将字段,表对应成了属性和类名字 即可。

六. HQL连接查询

HQL提供的连接方式:

迫切连接是指在指定连接方式时不仅指定了连接查询方式,而且显式地指定了关联级别的查询策略。

Hibernate 使用 fetch 关键字实现,fetch 关键字表明“左边”对象用于与“右边”对象关联的属性会立即被初始化

平时项目中都建议使用 fetch !

 

多对一关联,无迫切连接和迫切连接比较(其他连接类同):

sql 中 表名 inner join 表名

hqlPOJO类名 inner join 关联 属性类

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "from Student s inner join s.teacher t where s.id<? and t.id<?";
		//返回结果为 Object[]的List集合
		List<Object[]> list = session.createQuery(hql).setParameter(0, 6).setParameter(1, 5).list();
		for (Object[] object : list) {
			System.out.println(Arrays.toString(object));
		}
	}


	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "from Student s inner join fetch s.teacher t where s.id<? and t.id<?";
		//返回结果为 POJO类的List集合
		List<Student> list = session.createQuery(hql).setParameter(0, 6).setParameter(1, 5).list();
		for (Student student : list) {
			System.out.println(student);
		}
	}


--------
Student [id=1, sname=学生1, teacher=Teacher [id=1, tname=老师1]]
Student [id=2, sname=学生2, teacher=Teacher [id=1, tname=老师1]]
Student [id=3, sname=学生3, teacher=Teacher [id=3, tname=老师3]]
Student [id=4, sname=学生4, teacher=Teacher [id=3, tname=老师3]]
Student [id=5, sname=学生5, teacher=Teacher [id=4, tname=老师4]]

 

	
	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "select s from Student s inner join s.teacher t where s.id<? and t.id<?";
		//返回结果为 POJO类的List集合, 多条sql语句
		List<Student> list = session.createQuery(hql).setParameter(0, 6).setParameter(1, 5).list();
		for (Student student : list) {
			System.out.println(student);
		}
	}


        @SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "select s from Student s inner join fetch s.teacher t where s.id<? and t.id<?";
		//返回结果为 POJO类的List集合, 一条sql语句
		List<Student> list = session.createQuery(hql).setParameter(0, 6).setParameter(1, 5).list();
		for (Student student : list) {
			System.out.println(student);
		}
	}



--------

Hibernate: 
    select
        student0_.id as id1_3_0_,
        teacher1_.id as id1_4_1_,
        student0_.sname as sname2_3_0_,
        student0_.teacher_id as teacher_3_3_0_,
        teacher1_.tname as tname2_4_1_ 
    from
        t_student student0_ 
    inner join
        t_teacher teacher1_ 
            on student0_.teacher_id=teacher1_.id 
    where
        student0_.id<? 
        and teacher1_.id<?

使用结论:

      1. 使用 from.... 没加 fetch 和加了,得到的结果类型是不一样的,只发起一条 sql .

      2. 使用 select ...  指定查询对象时, 加 fetch,  只发起一条 sql , 反之发起多条 sql , 结果类型是一样。

根据发起的 sql 条数越来效率越高的原则,项目开发中, 推荐都加fetch

fetch关键字只对 inner join 和 left join 有效。right join 基本也不用.

 

七. HQL批量增删改

       每个 Hibernate Session 中都维持了一个必选的数据缓存,这个内部缓存正常情况下是由 Hibernate 自动维护的,并且没有容量限制。在批量插入与更新时,由于每次保存的实体都会保存在Session缓存中,当数据量大的时候,就可能出现OutOfMemoryException(内存溢出异常)。所以批量增加或更新操作中, 应该考虑到控制内部缓存的过度增长而出现OutOfMemeoryError 错误。

这里有两个方面进行控制:

一. 是在数据保存过程中周期性的对 Session 调用 flush 和 clear 方法,确保 Session 的容量不至于太大

二. 是设置 hibernate.jdbc.batch_size 参数来指定每次提交SQL的数量。

setParameter()     setParameterList()    executeUpdate()

1. 批量插入(不推荐):推荐使用 save()   

	@Test
	public void test() {
		List<Student> list = new ArrayList<Student>();
		for(int i=50; i < 85; i++) {
			Student student = new Student();
				student.setSname("学生" + i);
				list.add(student);
		}
		int row = 10;	//
		for(int i =0; i<list.size(); i++) {
			session.save(list.get(i));
			if(i % row == 0) {
				session.flush();
				session.clear();
			}
		}
	}

Session.flush():1.刷新所有数据;2.执行数据库SQL完成持久化动作;必须在操作结束且在提交事务和关闭连接之前被调用
Session.clear(): 则是清除 session 中的缓存数据。这样就达到控制session的一级缓存的大小

2. 批量修改

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "update Student s  set s.sname = ? where s.id > ?";
		int n = session.createQuery(hql).setParameter(0, "李斯").setParameter(1, 15).executeUpdate();
		System.out.println(n);
	}

3. 批量删除

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		Integer[] ids = {16,19,10}; //注意Object[]类型
		String hql = "delete from Student s where s.id in (:ids)";
		int n = session.createQuery(hql).setParameterList("ids", ids).executeUpdate();
		System.out.println(n);	}

-------
Hibernate: 
        delete 
    from
        t_student 
    where
        id in (
            ? , ? , ?
        )
3

批量操作总结:

1)优化 HIbernate API , 程序上采取分段插入或更新时 及时调用清除缓存方法。

2)通过  session.doWork()直接操作原生的 JDBC API 来做批量操作,性能更好。它具有以下优点:

     (1) 不会消耗大量内存。因为它不再将数据库中的大批量数据先加载到内存中,然后再逐个更新或修改。

     (2) 可以在一条SQL语句中更新或删除大批量的数据。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值