Hibernate检索1

举报
tea_year 发表于 2021/12/29 23:57:19 2021/12/29
【摘要】 Hibernate 的关联关系,通过关联关系能够对数据库表进行简单的操作。在大多数应用中,查询属于最重要的部分,而目前我们只能使用get方法和 load方法进行简单的查询,本章将主要讲解Hibernate的查询操作。  Hibernate 支持两种主要的查询方式。强大且易于使用的面向对象查询语言 HQL(Hibernate Q...

Hibernate 的关联关系,通过关联关系能够对数据库表进行简单的操作。在大多数应用中,查询属于最重要的部分,而目前我们只能使用get方法和 load方法进行简单的查询,本章将主要讲解Hibernate的查询操作。 

Hibernate 支持两种主要的查询方式。强大且易于使用的面向对象查询语言 HQL(Hibernate Query Language)。和Criteria查询。以及使用原生 SQL (native SQL)描述 Hibernate 查询。其中HQL是使用较为广泛的方式,也是我们本章学习的重点。


核心技能部分

1.1  Hibernate检索

在Hibernate 中,检索对象的方式包括:

1)导航图:根据已经加载的对象,导航到其他对象。例如,对于已经加载的Customer对象,调用它的getOrders().iterator()方法就可以导航到所有关联的Order对象,假如在关联级别使用了延迟加载检索策略,那么首次执行此方法时,Hibernate会从数据库中加载关联的Order对象,否则就从缓存中取得Order对象。 

2OID:按照对象的OID来检索对象。Sessionget()load()方法提供了这种功能。如果在应用程序中事先知道了OID,就可以使用这种检索对象的方式。

3HQL:Hibernate Query Language,它是完全面向对象的查询语句,查询功能非常强大,具备继承、多态和关联等特性。Hibernate官方推荐使用HQL进行查询。

4QBC:Query By Criteria,以对象的方式进行查询,将查询语句封装为对象操作。优点:可读性好,符合Java 程序员的编码习惯。缺点:不够成熟,不支持投影(projection)或统计函数(aggregation)。

5)本地查询:使用本地数据库的SQL查询语句。

1.2 HQL简介

1.2.1 为什么使用HQL

现在我们回忆一下我们在前一章学习中所遇到的查询问题,如何查询所有的版块?如何查询指定标题的帖子?显然是用我们已经掌握的get 或者load 这样的以id为条件进行查询的方式是无法做到的,是用HQL就可以轻松解决这样的问题。

现在我们使用HQL来检索所有的名称为鬼吹灯的帖子,代码如示例4.1所示。

示例4.1


  
  1. @Test
  2. public void hqlQuery1() throws Exception {
  3. String hql="from Thread as t where t.title like '%鬼吹灯%'";
  4. Query query = session.createQuery(hql);
  5. List<Thread> list = query.list();
  6. for(Thread t : list){
  7. System.out.println("帖子名称:"+t.getTitle()+
  8. " 内容:"+t.getContent());
  9. }

我们再回忆一下使用JDBC完成这一查询的情况,jdbc是面向数据库表的查询,查询出来的是一行行数据 一个一个的字段,还需要手工进行繁琐的数据提取和封装,才能得到我们需要的对象集合,而使用HQL则可以避免JDBC的这些弊端,提供了更简便和强大的对象化查询能力,而且HQL还是独立与数据库的,HQL语句不与具体数据库产品耦合。HQL具有以下功能:

(1)支持在查询语句中设定查询条件,动态绑定参数。

(2)支持投影查询。

(3)支持分页查询。

(4)支持连接查询。

(5)支持分组查询,能够使用关键字having和group by。

(6)内置聚集函数,如sum()、min()、max()等。

(7)可以调用用户自定义函数。

(8)支持子查询。

1.2.2 HQL入门

从示例4.1中我们可以总结出使用HQL的四个步骤:

1. 获取session

2. 编写HQL语句。

3. 创建Query对象。

4. 执行查询,获取结果。

HQL的完整的语法:

 

[select/update/delete...][from...][where...][ group by...][having...] [ order by . . . ]

HQL的语法和SQL非常的相似,但是不要被语法结构上的相似所迷惑,HQL具有鲜明的面向对象查询的特征,HQL是非常有意识的被设计为完全面向对象的查询,它可以理解如继承、多态 和关联之类的概念。在上例"from Thread as  t  where t.title  like '%鬼吹灯%'"   Thread是类名而不是表名,titleThread的属性名而不是字段名,查询出的也都是对象,在HQL的世界里没有数据库、没有表字段,有的只是对象。这就是HQLSQL的本质区别,也是需要重点理解和掌握的。

HQL还有一些基本的语法规则需要了解:

 HQL语句的关键字不区分大小写,但推荐小写。

 HQL中出现的类名,属性名严格区分大小写。

 可以为类设置别名,以供其他地方引用,例如 上例中的 as t。

 as 关键字是可选的, 一般别名推荐小写。

 from前也可以加select 但必须配合别名使用。

HQL非常的强大与复杂。实际上,Hibernate的一个主要卖点就是查询语句的威力。下面我们会向大家一一介绍。

1.3 实体查询

根据上述HQL的查询步骤,编写一个最简单的查询,代码如示例4.2所示

示例4.2


  
  1. String hql = “from Thread”;
  2. Query query = session.createQuery(hql);
  3. List< Thread> List = query. list ( );
  4. for ( Thread t : List ) {
  5. System.out.println("名称:”+t getTitle());
  6. }

上述代码中,hql = “from Thread”将取出 Thread对象所对应表的全部记录,对应的 SQL语句为: Select * from  tip

HQL还支持多态查询,如果实体之间存在继承关系,那么from Thread将会查询出所有的Thread和他的子类数据。例如 from java.lang.Object 将会返回所有的数据库表数据。

与 SQL语句相同,HQL也支持where子句。例如示例4.1中的"from Thread as  t  where t.title  like '%鬼吹灯%'"

Hibernate 的 HQL查询支持与SQL相同的各种运算,详见表 4-1-1所示。

4-1-1   HQL 支持的各种运算

运算类型

HQL运算符

含义

比较运算

=

等于

<>

不等于

>

大于

>=

大于等于

<

小于

<=

小于等于

Is null

为空

Is not null

不为空

范围运算

In

等于列表中的某一个值

Not in

不能与列表中的任意一个值

Between value1 and value2

大于等于值1,小于等于值2

Not between value1 and value2

小于值1或者大于值2

字符串模式匹配

Like

字符串模式匹配

逻辑运算

and

逻辑与

Or

逻辑或

Not

逻辑非

我们可以通过这些操作符为where子句指定条件,也可以通过and or等逻辑连接符组合各个条件,代码如示例4.3所示。

示例4.3


  
  1. @Test
  2. public void hqlQuery2() throws Exception {
  3. String hql="from Thread as t where t.title like '%鬼吹灯%' and t.dateCreated>'2010-07-10'";
  4. Query query = session.createQuery(hql);
  5. List<Thread> list = query.list();
  6. for(Thread t : list){
  7. System.out.println("帖子名称:"+t.getTitle()+
  8. " 内容:"+t.getContent());
  9. }
  10. }

这些操作符的使用和SQL都没什么区别,只需要把握HQL面向对象的精神即可,这里不再赘述。

1.4 属性查询

现在我们使用HQL可以轻松的查询到我们需要的对象,但在某些情况下,我们并不需要取得完整的实体对象,如在下拉列表中显示出版块的名称,或者在加载帖子列表的时候,我们只需要帖子的标题等信息 而例如content这样比较大的属性并不需要加载,此时我们需要的数据可能仅仅是对象的某一个或者某几个属性,通过HQL我们可以轻松的做到这一点!代码如示例4.4所示。

示例4.4


  
  1. @Test
  2. public void hqlQuery3() throws Exception {
  3. String hql="select t.title from Thread as t";
  4. Query query = session.createQuery(hql);
  5. List<String> list= query.list();
  6. for(String title: list){
  7. System.out.println("帖子标题:"+title);
  8. }
  9. }

单一属性查询,返回结果集属性列表,元素类型和实体类中相应的属性类型一致,但如果是对多个属性的查询HQL查询返回的结果又是什么呢?,多个属性的查询如示例4.5所示。

示例4.5


  
  1. @Test
  2. public void hqlQuery3() throws Exception {
  3. String hql="select t.title,t.dateCreated from Thread as t";
  4. Query query = session.createQuery(hql);
  5. List<Object[]> list = query.list();
  6. for(Object[] objects: list){
  7. System.out.println("帖子标题:"+objects[0]+" 发布时间:"+objects[1]);
  8. }
  9. }

多个属性查询,返回的集合元素是对象数组,数组元素的类型和对应的属性在实体类中的类型一致,数组的长度取决于select中属性的个数。

另外还有一种更加面向对象化的属性查询方式 如示例4.6所示。

示例4.6


  
  1. @Test
  2. public void hqlQuery5() throws Exception {
  3. String hql="select new Thread(th.title,th.hit) from Thread as th";
  4. Query query = session.createQuery(hql);
  5. List<Thread> list = query.list();
  6. for(Thread t: list){
  7. System.out.println("标题:"+t.getTitle()+
  8. " 点击量:"+t.getHit());
  9. }
  10. }

这种方式能够在HQL语句中动态的构造对象,并封装我们所需要的属性数据,这时候list中保持的是对象,使用这种方式需要注意以下两点:

 类必须有相应的构造函数。

 除了在构造的时候赋予的属性值外,其他属性都是null

 在MySQL数据库中如果是日期类型,譬如:dateCreated,实体类的属性应是java.util.Date,在构造方法中对其赋值。MyEclipse生成的是java.sql.Timestamp类型的。

1.5 参数绑定

在前面的应用中,我们的查询条件都是直接在HQL中表达,例如:select info from Thread as info where info.goodsName='手机',在实际应用中查询的条件肯定是在不断变化的,解决这个问题的最直接办法就是拼接字符串,但这种方式很容易遭到SQL注入的攻击,安全性很差而且执行效率低。现在我们回忆一下在JDBC中是如何解决这个问题的?

JDBC中我们是使用PrepareStatement对象进行了参数的动态绑定,HQL也提供了类似的参数绑定方式。

HQL中的参数绑定主要有三种形式:

 按位置绑定。

 按名称绑定。

 封装参数。

4.5.1 按照位置绑定

代码如示例4.7所示。

示例4.7


  
  1. @Test
  2. public void hqlQuery6() throws Exception {
  3. String hql="from Thread as th where th.topped=? and th.title like ?";
  4. Query query = session.createQuery(hql);
  5. query.setBoolean(0,true);
  6. query.setString(1, "%鬼吹灯%");
  7. List<Thread> list = query.list();
  8. for(Thread th: list){
  9. System.out.println("标题:"+th.getTitle()+
  10. " 置顶:"+th.getTopped());
  11. }
  12. }

使用这种方式绑定参数需要注意如下几点:

 必须按照参数的顺序,调用相应的setType()方法赋值。

 参数的下标从0开始。

 如果有多个参数的时候,必须保证每个参数都被绑定值。

4.5.2 按照名称绑定

代码如示例4.8所示。

示例4.8


  
  1. @Test
  2. public void hqlQuery7() throws Exception {
  3. String hql="from Thread as th where th.topped=:top and th.title like :name";
  4. Query query = session.createQuery(hql);
  5. query.setBoolean("top",true);
  6. query.setString("name","%鬼吹灯%");
  7. List<Thread> list = query.list();
  8. for(Thread th: list){
  9. System.out.println("标题:"+th.getTitle()+
  10. " 置顶:"+th.getTopped());
  11. }
  12. }

使用按照名称进行绑定,请注意以下几点:

 在HQL查询语句中定义命名参数时以 “:”开头。

 Query提供的方法能绑定各种类型的参数。此类 setXXX()方法中,第一个参数用于设置各种类型的命名参数,第二个参数表示命名参数的值。

这种方式有比较好的可读性,可以避免因大意而产生的参数顺序错误。

4.5.3 封装参数

当需要绑定的参数非常多,那么无论使用按位置,还是按名称绑定参数都会非常的繁琐,HQL中提供了第三种方式进行参数的绑定。即先将参数值封装到bean中,然后将beanQuery进行绑定。代码如示例4.9所示。

示例4.9


  
  1. @Test
  2. public void hqlQuery8() throws Exception {
  3. String hql="from Thread as th where th.title like :title and "+
  4. "th.hit between:low_hit and :high_hit and th.replyCount>:rcount and th.topped=:top";
  5. Query query = session.createQuery(hql);
  6. Map queryProperties = new HashMap();
  7. queryProperties.put("title","%java%");
  8. queryProperties.put("high_hit",100000);
  9. queryProperties.put("low_hit",10);
  10. queryProperties.put("rcount",10);
  11. queryProperties.put("top",false);
  12. query.setProperties(queryProperties);
  13. List<Thread> list = query.list();
  14. for(Thread th: list){
  15. System.out.println("标题:"+th.getTitle()+
  16. " 点击量:"+th.getHit()+" 回帖:"+th.getReplyCount());
  17. }
  18. }

这种方式一定要注意封装参数值的bean中的属性名一定要和HQL语句中的命名参数名称一致,这样Hibernate才能根据名称进行匹配。

1.6 使用聚合函数

HQL中聚合函数的使用和SQL中基本一致。

1.6.1 count()

查询指定用户所发布的帖子总数,代码如示例4.10所示。

示例4.10


  
  1. @Test
  2. public void hqlQuery9() throws Exception {
  3. String hql="select count(th) from Thread as th where th.author.id=1";
  4. Query query = session.createQuery(hql);
  5. Long count=(Long) query.uniqueResult();
  6. System.out.println("用户发帖: "+count);
  7. }

本例中使用了count()函数来计算总数,显然查询的结果不肯能有多个,这个时候再使用list方法求的集合,并从中取得数据,就显得很繁琐,这时候可以使用uniqueResult()来取得唯一结果,这里需要注意的是,如果返回的结果大于1个本方法将会抛出异常。

1.6.2 max()min()

求得所有帖子中最大,最小点击量。代码如示例4.11所示。

示例4.11


  
  1. @Test
  2. public void hqlQuery10() throws Exception {
  3. String hql="select max(th.hit),min(th.hit)from Thread as th";
  4. Query query = session.createQuery(hql);
  5. List<Object[]> list = query.list();
  6. Object[] object = list.get(0);
  7. System.out.println("单贴最高点击: "+object[0]);
  8. System.out.println("单贴最低点击: "+object[1]);
  9. }


1.6.3 avg()sum()

userid=1的用户所发布信息的总点击量和平均点击量,代码如示例4.12所示。

示例4.12


  
  1. @Test
  2. public void hqlQuery11() throws Exception {
  3. String hql="select avg(th.hit),sum(th.hit)from Thread as th where th.author.id=1";
  4. Query query = session.createQuery(hql);
  5. List<Object[]> list = query.list();
  6. Object[] object = list.get(0);
  7. System.out.println("平均点击量: "+object[0]);
  8. System.out.println("总点击量: "+object[1]);
  9. }

1.7 排序

与 SQL相同,HQL可以通过order by子句实现对查询结果的排序,代码如下。

  from  Thread  as  th  order  by  th.datecreated  desc;

order by子句可以指定多个排序条件,测试代码如下。

 from Thread  as  th  order by th . hit ,th. datecreated desc;

1.8 分组

HQL也可以像SQL一样使用group by子句进行分组统计,例如 我们根据帖子类型进行数量统计。代码如示例4.13所示。

示例4.13


  
  1. @Test
  2. public void hqlQuery12() throws Exception {
  3. String hql = "select count (th) , th.type from Thread as th group by th.type";
  4. Query query = session.createQuery(hql);
  5. List< Object[]> list = query.list();
  6. for(Object[] objects : list){
  7. System.out.println( "数量:"+objects[0]+"类型: "+objects[1] );
  8. }
  9. }

having子句可以对group by子句进行甄选。例如,对帖子数量大于8的种类的分类统计,代码如示例4.14所示。

示例4.14


  
  1. @Test
  2. public void hqlQuery13() throws Exception {
  3. String hql = "select count (th) , th.type from Thread as th group by th.type having count(th)>8";
  4. Query query = session.createQuery(hql);
  5. List< Object[]> list = query.list();
  6. for(Object[] objects : list){
  7. System.out.println( "数量:"+objects[0]+"类型: "+objects[1] );
  8. }
  9. }


1.9 分页查询

使用JDBC进行数据库的分页查询的语句很复杂,而且每种数据库产品都有特有的分页语句,程序与具体数据库绑定,造成系统的移植和扩展困难,而Hibernate为我们提供了简便、统一的分页方式,主要通过Query的以下两个方法实现:

Ø setFirstResult(int firstResult):设定从哪一个对象开始检索,参数firstResult表示这个对象在查询结果中的索引位置,索引位置的起始值为0。默认情况下,Query接口从查询结果中的第一个对象,也就是索引位置为0的对象开始检索。

Ø setMaxResult(int maxResults):设定一次最多检索出的对象数目。默认情况下,Query接口检索出查询结果中所有的对象。

具体代码如示例4.15所示。

示例4.15

Query query =session.createQuery("from Thread  as th ");

query.setFirstResult(0);

query.setMaxResults(10);

List result = query.list();

 

1.10 子查询

对于支持子查询的数据库,Hibernate支持在查询中使用子查询。他可以在查询中使用另外一条查询的结果,一个子查询必须出现在where子句中且被圆括号包围起来(经常是SQL聚集函数的圆括号)甚至相互关联的子查询(引用到外部查询中的别名的子查询)也是允许的。

查询点击量最大的帖子。代码如示例4.16所示。

示例4.16

@Test

public void hqlQuery14() throws Exception {

String hql = "select th from Thread as th where th.hit=(select max(hit) from Thread)";

    Query query = session.createQuery(hql);

    List< Thread> list = query.list();

    for(Thread th : list){

       System.out.println( "点击量:"+th.getHit()+"帖子标题: "+th.getTitle() );

    }

}

运行后控制台输出如图4.1.11所示。

 

4.1.11 子查询


本章总结

 

Ø Hibernate 中,检索对象的方式

n 导航图:根据已经加载的对象,导航到其他对象。

n OID:按照对象的OID来检索对象。Sessionget()load()方法

n HQL:Hibernate Query Language

n QBC:Query By Criteria

n 本地查询:使用本地数据库的SQL查询语句。

 HQL

n HQL实体查询

n HQL属性查询

n HQL参数绑定

n HQL实现查询排序

n HQL实现查询分组

n HQL实现查询分页



选择题

1. setMaxResults(3)方法中,参数值3是指( )。

    A. 从第 3条记录开始

    B. 从第4条记录开始

    C. 查询 3条记录

    D. 查询4条记录

2. Hibernate 中,下面代码实现了对Book实体中title属性的模糊查询说法正确的是 ( )。

Session session=this.getSession ( );                      

String  hql=”from  Book  model  where  modeL. title like?”;  // (1)

Query query =session.createQuery (hql );// (2)

query.setString(0“%张明%”);// (3)

List list = query.list ( );// (4)

A. 第(1)行中,Book与 model之间必须有as关键字

B. 第(2)行中没有错误

C. 第(3)行应该为:query.setString(0“张明”);

D. 第(4)行应该为:List llist = query.executeQuery();

3. 关于HQL查询,下面说法中错误的是()

    A. HQL查询的 select子句中必须区分大小写

    B. HQL支持统计函数

    C. HQL支持仅查询对象的某几个属性,查询结果保存于Object数组中

    D. HQL语句可以实现类似PreparedStatement 的效果

4. 下面代码的执行效果是()。

String hql=”from Student s order by s.score asc”;

Query query = session.createQuery(hql);

query.setFirstResult(0);

query.setMaxResult(5);

return query.list();

A.返回分数最高的五个学生。

B.  返回分数最高的六个学生。

C.返回分数最低的五个学生。

D.  返回分数最低的六个学生。

5. 下面HQL语句的含义是()。

Select stu  from Student stu where stu.score>(select avg(score) from Student)

A. 查询所有学生的平均分。

B.   查询得分大于平均分的学生成绩。

C. 查询得分最高的学生。

D.   查询得分大于平均分的学生

上机练习

在上机的基础上完成对帖子的回复功能 ,点击帖子可以分页显示帖子的详细内容和他的所有回复,并在添加回复后及时更新帖子的最后回复时间和版块的最后回复。



文章来源: aaaedu.blog.csdn.net,作者:tea_year,版权归原作者所有,如需转载,请联系作者。

原文链接:aaaedu.blog.csdn.net/article/details/79831188

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。