Hibernate 5.3(七)

Hibernate 查询

Hibernate提供异常强大的查询体系,使用Hibernate 有多种查询方式可以选择:既可以使用Hibermate的HQL查询,也可以使用条件查询,甚至可以使用原生的SQL查询语句。不仅如此,Hibernate还提供了一种数据过滤功能,这些都用于筛选目标数据。

HQL 查询

HQL 简介

HQL是Hibernate Query Language 的缩写,HQL的语法很像SQL的语法,但HQL是一种面向对象的查询语言。SQL的操作对象是数据表、列等数据库对象,而HQL的操作对象是类、实例、属性等。

HQL是完全面向对象的查询语言,因此可以支持继承、多态等特性。

HQL语句本身是不区分大小写的。也就是说,HQL语句的关键字、函数都是不区分大小写的。但HQL语句中所使用的包名、类名、实例名、属性名都区分大小写。

HQL 查询步骤
  1. HQL查询依赖于Query类,每个Query实例对应一个查询对象。
  2. 获取Hibernate Session对象。
  3. 编写HQL语句。
  4. 以HQL语句作为参数,调用Session的createQuery方法创建查询对象。
  5. 如果HQL语句包含参数,则调用Query的setXxx方法为参数赋值。
  6. 调用Query对象的list等方法返回杳询结果列表(持久化实体集)。
HQL 牛刀小试

在这我给出映射文件,持久化比较简单,就不给出了,自己根据映射文件,去创建相应的持久化类。

Person.hbm.xml:

<hibernate-mapping package="com.example.test.bean">
	<class name="Person" table="PERSON" dynamic-insert="true"
		dynamic-update="false" >
		<id name="id" type="integer" column="person_id">
			  <generator class="identity"></generator>
		</id>
		<property name="name" type="string" column="person_name"></property>
		<property name="gender" type="string" column="person_gender"></property>
		<set name="events" table="person_event"><!-- 多对多的关系 连接表 -->
		   <key column="person_id"></key><!-- 外键列 -->
		   <many-to-many class="Event" column="event_id"></many-to-many><!-- 外键列 -->
		</set>
	</class>
</hibernate-mapping>

<hibernate-mapping package="com.example.test.bean">
<class name="Event" table="event_info">
     <id name="event_id" column="event_id">
     <generator class="identity">
     </generator>
     </id>
     <property name="event_date" type="date" column="event_date"></property>
     <property name="event_title" type="string" column="event_title"></property>
     <set name="persons" table="person_event">
        <key column="event_id"></key><!-- 外键列   -->
        <many-to-many class="Person" column="person_id"></many-to-many><!-- 外键列 -->
     </set>
</class>
</hibernate-mapping>
		List list = ss
				.createQuery(
						"select p from   Person as p join p.events where event_title =:event_title1")
				.setString("event_title1", "问问我").list();
		java.util.Iterator<Person> it = list.iterator();
		while (it.hasNext()) {

			System.out.println(it.next().getName());
		}
		java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(
				"yyyy-MM-dd");
		Date d = null;
		Date d1 = null;
		try {
			d = sdf.parse("2018-09-16");
			d1 = sdf.parse("2018-09-30");

		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		List list = ss
				.createQuery(
						"select distinct p from Person as p join p.events event where event.event_date between :date1 and :date2 ")
				.setDate("date1", d).setDate("date2", d1).list();
		java.util.Iterator<Person> it = list.iterator();
		while (it.hasNext()) {

			System.out.println(it.next().getName());
		}

		List list = ss
				.createQuery(
						"select distinct p.id,p.name,p.gender from Person p join p.events")
				.list();
		java.util.Iterator<Object> it = list.iterator();
		while (it.hasNext()) {
			Object[] obj = (Object[]) it.next();
			System.out.println(java.util.Arrays.toString(obj));
		}

这里只是带你体验一把HQL 语句的使用,数据库的数据,自己随便准备,下面才是真正的讲解,里面有不懂的,继续向下看即可。

HQL 查询的where 子句

from是最简单的HQL语句,也是最基本的HQL语句。from关键字后紧跟持久化类的类名。

select distinct p.id,p.name,p.gender from Person p join p.events

大部分时候,推荐为持久化类的每个实例起别名。既然是实例的别名,命名也符合Java的命名规则:

第一个单词的首字母小写,后面每个单词的首字母大写。

关联和连接

当程序需要从多个数据表中取得数据时,SQL语句将会考虑使用多表连接查询。Hibemate使用关联映射来处理底层数据表之间的连接,一旦我们提供了正确的关联映射后,当程序通过Hiberate进行持久化访问时,将可利用Hibernate的关联来进行连接。

HQL支持两种关联连接(join) 形式:

  1. 隐式(implicit)

隐式连接形式不使用join关键字,使用英文点号(.) 来隐式连接关联实体,而Hibernate底层将自动进行关联查询。

select p from Person as p  where p.events.event_title =:event_title1//这里实际上查看持久化类events 是一个集合,实际上,就是通过它知道映射关系,底层会自动关联,集合里面都是Event 对象,拿到event_title  属性的。
  1. 显式(explicit)
select p from Person as p join p.events where event_title =:event_title1

使用显式连接时可以为相关联的实体,甚至是关联集合中的全部元素指定一个 别名。

select p from Person as p join p.events as event where event.event_title =:event_title1

使用显式连接时,还可通过HQL的with关键字来提供额外的连接条件。

select p from Person as p join p.events  with p.id >event.event_id  where event_title =:event_title1 

数据库具体到底使用哪种连接方法,则取决于HQL语句的显式连接使用了哪种连接方式。

select p from Person as p   where p.events.event_title =:event_title1

如果events是普通组件属性,或单个的关联实体,则Hibernate会自动生成隐式内连接,上面 HQL语句依然有效。
如果events是一个集合(包括1-N、N- N关联),那么系统将会出现QueryException异常,异常提示信息为: ilegal attempt to dereference collection。必须显式哟纪念馆join 关键字。

隐式连接和显式连接查询后返回的结果不同。

当HQL语句中省略select关键字时,使用隐式连接查询返回的结果是多个被查询实体组成的集合。

from Person as p  join p.events event//显式查询

当使用显式连接查询的HQL语句中省略select关键字时,返回的结果也是集合,但集合元素是被查询持久化对象、所有被关联的持久化对象所组成的数组。上面的HQL语句,查询到的结果集的每条记录既包含了Person实体的全部属性,也包含了Event 实体的全部属性。属于Person的属性创建Person对象,属于Event的属性创建Event对象,每一条记录里面都包含一个person对象和Event对象,每一条记录对应一个数组,最后将每一个记录对应成一个集合的元素。

在这里插入图片描述

如果你只想得到某一个实体得到的集合,并且不重复。

select distinct p from Person p join p.events

这样得到的实体,就只是Person 实体,并且通过distinct 关键字来保证关联查询的数据,不重复。

HQL 结果返回
List list = ss.createQuery("select distinct p from Person p join p.events").list();
		   tt.commit();
		   ss.close();
		   sf.close();
		   java.util.Iterator<Person> it = list.iterator();
		  while (it.hasNext()) {
			Person p = (Person) it.next();
			System.out.println(p.getName());
		}

我们之前用hiberante 查询,一旦查询的session 关闭,就无法在查询结果,但是使用的HQL 查询,查询之后返回的Query 是把结果放在集合对象中,所以,你关闭session对象,还可以通过该对象去访问,至于该对象回收,那就是Java 的垃圾回收器的问题。虽然上述,我们不习惯在使用关闭session,但是在这你要知道,即使关闭,你也可以使用。

HQL 查询fetch

我们之前hibernate 查询默认都是懒加载,就是关联的对象和集合属性属性无法直接加载,之前我们可以在配置文件中设置hibernate lazy 进行设置,我们同样可以在HQL 语句中进行设置。

List list = ss.createQuery("select distinct p from Person p join fetch p.events").list();
		java.util.Iterator<Person> it = list.iterator();
		while (it.hasNext()) {
			Person p = (Person) it.next();
			System.out.println(p.getName());
			Set<Event> set = p.getEvents();
			for(Event e:set){
				System.out.println(e.getEvent_title());
				//Exceptopn:failed to lazily initialize a collection of role
			}
		}

上述如果没有访问,没有fetch,则会报出异常。

使用fetch关键字时有如下几个注意点:

  • fetch不应该与setMaxResults()或setFirstResult()(这两个属性是用来分页显示查询的)共用。因为这些操作是基于结果集的,无法预先知道精确的行数。fetch 是将查询的实体关联的对象,都查询出来。

fetch不能与独立的with条件一起使用。因为fetch 查询出来的是关联实体所有结果,with 是在结果的筛选条件,两者冲突。

如果在映射文件映射属性时通过指定lazy= "true"启用了延迟加载(这种延迟加载是通过字节码增强来实现的),然后程序里又希望预加载那些原本应延迟加载的属性,则可以通过fetch all properties来强制Hibermate立即抓取这些属性。

HQL 查询select 子句

select可以选择持久化类的直接属性,还可以选择组件属性包含的属性以及持久化实例(就是该持久化类的所有属性),当然select选择的必须是属于from后面持久化类。

在通常情况下,使用select 子句查询的结果是集合,而集合元素就是select 后的实例、属性等组成的数组。

即使select后的列表项选出某个持久化类的全部属性,这些属性依然是属性,Hibernate不会将这些属性封装成对象。只有在select后的列表里给出持久化类的别名(其实就是实例名), Hibernate才会将该项封装成一个持久化实体。

select p from Person p 

这时需要注意虽然查询该实例的所有属性,但是无法访问集合元素,如果强制访问,会报failed to lazily initialize a collection of role,也就是上面说的懒加载。

select支持将选择出的属性存入一个List对象
  List list = ss.createQuery("select  new list(p.name ,p.id) from Person p ").list();
		   tt.commit();
		   ss.close();
		   sf.close();
		java.util.Iterator<Object> it = list.iterator();
		while (it.hasNext()) {
			List list1 = (List) it.next();//正常的查询结果是封装在list,如果里面有多个属性,那么
			//是封装成数组,存放在list中;而这里对于多个属性,则是又封装在list中,存放在外面的list中。
			System.out.println("姓名"+list1.get(0));
			System.out.println("id"+list1.get(1));
		}
select支持将选择出的属性存入一个Map对象
List list = ss.createQuery("select new map(p.name as personname) from Person p  ").list();
		   //这里给select 后面查询的设置别名,一般该别名和map 使用,作为map 对象的键,而实际查询
		   //查询出来的值是作为map的value。
		   tt.commit();
		   ss.close();
		   sf.close();
		java.util.Iterator<Map<String,String>> it = list.iterator();
		while(it.hasNext()){
			Map p = (Map) it.next();//这里每次产生都是一个新的map 对象,所以用一个key,没有问题
			System.out.println(p.get("personname"));
		}
select支持将选择出的属性存入一个对象
public class QueryEntity {
	private Integer id;
	private String name;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public QueryEntity(Integer id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
}

这个需要提供对应类型的构造函数,必须一致。

 //这里必须指定对象包路径,不然后找不到实体类
		   List list = ss.createQuery("select new com.example.test.bean.QueryEntity(p.id,p.name) from Person p  ").list();
		   tt.commit();
		   ss.close();
		   sf.close();
		   java.util.Iterator<QueryEntity> it = list.iterator();
		   while (it.hasNext()) {
			QueryEntity qe = (QueryEntity) it.next();
			System.out.println("姓名"+qe.getName());
			System.out.println("id"+qe.getId());
		}
HQL 聚集函数
  • avg: 计算属性平均值。
  • count: 统计选择对象的数量。
  • max:统计某个属性值的最大值。
  • min: 统计某个属性值的最小值。
  • sum: 计算某个属性值的总和。
List list = ss.createQuery("select count(*) from Person p").list();//返回查询的结果总条数
		   tt.commit();
		   ss.close();
		   sf.close();
		   java.util.Iterator<QueryEntity> it = list.iterator();
		   while (it.hasNext()) {
			System.out.println(it.next());
		}
多态查询

HQL支持在from子句中指定任何Java 类或接口,查询会返回继承了该类的所有持久化子类的实例。前提,你需要在映射文件进行正确的继承映射。

where 查询子句

Person 定义一个组件属性

private Name name1; 
List list = ss.createQuery("select p.name1.first from Person p").list();//虽然组件属性在数据库表中,是在person表中,但是不可以直接p.first 
//必须直接从Person 的属性访问复合属性,在访问复合属性的属性。
		   tt.commit();
		   ss.close();
		   sf.close();
		   java.util.Iterator<String> it = list.iterator();
		   while (it.hasNext()) {
			System.out.println(it.next());
		   }

我们可以通过点号来隐式查询多张数据库的表,前提不是集合属性,只要该类持有关联类的对象

(select phone_name from Phone p where p.person.name=:name1 ").setParameter("name1","laoqiang").list()

在该HQL中,Phone 类持有Person 对象,可以直接关联查询,如果是多个表中都存在这样的关系,会关联多个表查询。而之前我们说的隐式关联,都是通过集合去关联查询,其实都一样,只是出发点不同而已。

HQL 中= 可以用来用给属性赋值,同时还可以比较实例。这里有人,肯定会想一张表就是一个实例,每次查询的都是一张表,怎么可能是多个实例,那问题简单,我们就给它多张表看看(注意这里的多张,不是关联的啦)

 from Phone p,Person p1//注意这种方式,不是查询多个表。

我们执行该HQL,发现数据库执行的语句是一条cross join ,是笛卡尔积,将两个相乘起来。

在where 子句中,id可以表示一个对象的标识符,也就是主键,比较方便。(这里也可以使用对象的主键属性,两者选择其一)

(" select p.number from Phone p where p.id =:id1").setParameter("id1",2).list()

如果你是通过id 去引用主键,那么如何引用组件主键呢?

一定不要忘记组件类作为主键,该类需要实现序列化接口,同时必须重写属性的hashcode和equals。

               <composite-id class="PersonId" name="personId"><!--用来定义组件主键 ,class 用来指定用PersonId 类生成组件,name 是属性值 -->
		<key-property name="a" column="a" type="integer">
		</key-property>
		<key-property name="b" column="b" type="integer">
		</key-property>
		</composite-id>
(" select p.name from Person p where p.id.a =:a1").setParameter("a1",1).list();

在映射表具有继承关系的时候,我们一般在where 子句中使用class 去判断某个实例。class 的值就是discriminator-value 配置name(就是实例的类名)。

<hibernate-mapping package="com.example.test.bean">
	<class name="Person1" table="PERSON" dynamic-insert="true">
           <id name="id" column="id" type="integer">
             <generator class="identity"></generator>
           </id>	
           <discriminator column="type" type="string"></discriminator>
           <property name="name" type="string" column="name"></property>
          <!--  指定继承关系中鉴别值 -->
           <subclass discriminator-value="man" name="Man">
           <property name="manonly" type="string" column="manonly"></property>
           </subclass>
           <subclass discriminator-value="woman" name="Woman">
           <property name="womanonly" type="string" column="womanonly"></property>
           </subclass>
	</class>
</hibernate-mapping>
 select p.name from Person1 p where p.class = Man

在where 子句中一个实例(表)查询,有多个条件用and 连接。

当where 子句中的运算符只支持基本类型或者字符串时,where 子句中的属性表达式必须以基本类型或者字符串结尾,不要使用组件类型属性结尾。

表达式

演示的一些例子,是以字段,在HQL中使用,需要属性名。

hql 的where 子句可以包含很多的表达式进行查询,但是需要注意的是,有些需要底层数据库的支持,稍微注意一下。

  1. 数学运算符: +、一、*、/等。基于字段是数值类型的操作。

  2. 二进制比较运算符: =、>=、<=、<>(不相等)、!=(不相等)、like等。(这里有一个要注意,就是日期类型也是可以用>比较的)

  3. 逻辑运算符: and、 or、 not等。

  4. 字符串连接符:如value1 Il value2,或使用字符串连接函数concat(value1,value2)。里面的两个值是字段名,将字段值拼接起来。

  5. 查询时间: current_ date(). current_ time(). current_ timestamp(). second(). minute().hour(). day(). month(). year()
    在这里插入图片描述
    6.字段值的操作函数: substring() trim() lower() upper() length() locate() abs() sqrt() bit length() coalesce()f nullif()
    在这里插入图片描述

  6. HQL语句支持使用英文问号(?)作为参数占位符,这与JDBC的参数占位符一致;命名参数占位符号,方法是在参数名前加英文冒号(? ,例如:start date, :x1 等。

  7. 可在where子句中使用SQL常量,例如"foo’、69、'1970-01-01 10:00:01.0’等 。还可以在HQL语句中使用Java中的public static final类型的常量,例如Color.RED。

  8. in. not in. [not]between …and(这个后面是字段值,查询是会根据数据库表的排列顺序,返回两个字段值其中的数据) . is null., is not null. member of and not memberof (这个在sql 有多态查询,判断一个表实例是否是另外一个实例 Man extends Person man member of person ),注意在hql 中的字符串是需要单引号括起来。

  9. 下面的声明表明: HQL 转换SQL语句时,将使用字符1和0来取代关键字true和false, 然后将可以在表达式中使用布尔表达式。

<property I name"hibernate. query. substitutions">true 1,! false 0</property>
from Cat cat where cat.alive =0;
  1. 对于有序集合(普通属性会报错),还可使用minindex 与maxindex函数代表最小与最大的索引序数。同理,可以使用minelement与maxelement 函数代表集合中最小与最大的元素(这个一定是数值类型,才可以使用).
  2. size关键字用于返回一个集合的大小。
cat where cat.kittens.size > 0;
cat where size (cat.kittens) > 0;
  1. 在where子句中,有序集合(数组、List集合、Map对象)的元素可以通过[ ]运算符访问。如果是List,[数字];如果是Map,[key]。
  2. elements 可以返回指定集合中所有元素,indices 可以返回指定集合的所有索引。
("select elements(p.phones) from Person p where p.name=:name1").setParameter("name1","laoqiang1")//查询名字为laoqiang1 的phone 信息,执行的时候,会发现底层数据库执行的笛卡尔积运算,查询出来phone表满足的主键id,在根据id 去查询phone信息。
select
        phones1_.phone_id as col_0_0_ 
    from
        PERSON person0_ cross 
    join
        PHONE phones1_ 
    where
        person0_.person_id=phones1_.person_id 
        and person0_.person_name=?
"select p1.name from Phone p,Person p1 where p in elements(p1.phones) and p.id=3"//查询Phone 中id=3 的Person 的名字,通过啥关联的就是通过 p  in elements(p1.phones)来确定了 p 对应的Person ,从来产生联系,就可以查询到Person了 
order by

在HQL 可以和SQL 对结果集进行排序。asc 升序 desc 降序,如果不指定,那么默认是升序。

select p1.name from Person p1 **order by** p1.name desc
group by

在HQL 可以和SQL 对结果集进行分组。

select p1.name from Person p1 group by  p1.name 

注意分组的属性一定要出现在select 后面。

having

是对分组的结果进行筛选。

select p.color from Person p group by p. color having p.color in ('yellow','red')

having子句用于对分组进行过滤,因此having子句只能在有group by子句时才可以使用, 没有group by子句,不能使用having子句。

子查询

HQL 的子查询只能出现在where或者select 中。

命名查询

HQL查询还支持将查询所用的HQL语句放入配置文件中,而不是代码中。

在Hibernate映射文件的<hibermate -mappng…元素中使用<query…>子元素来定义命名查询,使用<query…>元素只需指定个name属性,指定该命名查询的名字。

<query name="query1">
	select p from Person p
</query>
		 java.util.List list = ss.getNamedQuery("query1").list();

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值