Hibernate检索1
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对象。
(2)OID:按照对象的OID来检索对象。Session的get()和load()方法提供了这种功能。如果在应用程序中事先知道了OID,就可以使用这种检索对象的方式。
(3)HQL:Hibernate Query Language,它是完全面向对象的查询语句,查询功能非常强大,具备继承、多态和关联等特性。Hibernate官方推荐使用HQL进行查询。
(4)QBC:Query By Criteria,以对象的方式进行查询,将查询语句封装为对象操作。优点:可读性好,符合Java 程序员的编码习惯。缺点:不够成熟,不支持投影(projection)或统计函数(aggregation)。
(5)本地查询:使用本地数据库的SQL查询语句。
1.2 HQL简介
1.2.1 为什么使用HQL
现在我们回忆一下我们在前一章学习中所遇到的查询问题,如何查询所有的版块?如何查询指定标题的帖子?显然是用我们已经掌握的get 或者load 这样的以id为条件进行查询的方式是无法做到的,是用HQL就可以轻松解决这样的问题。
现在我们使用HQL来检索所有的名称为鬼吹灯的帖子,代码如示例4.1所示。
示例4.1
-
@Test
-
-
public void hqlQuery1() throws Exception {
-
-
String hql="from Thread as t where t.title like '%鬼吹灯%'";
-
-
Query query = session.createQuery(hql);
-
-
List<Thread> list = query.list();
-
-
for(Thread t : list){
-
-
System.out.println("帖子名称:"+t.getTitle()+
-
-
" 内容:"+t.getContent());
-
-
}
我们再回忆一下使用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是类名而不是表名,title是Thread的属性名而不是字段名,查询出的也都是对象,在HQL的世界里没有数据库、没有表字段,有的只是对象。这就是HQL和SQL的本质区别,也是需要重点理解和掌握的。
HQL还有一些基本的语法规则需要了解:
HQL语句的关键字不区分大小写,但推荐小写。
HQL中出现的类名,属性名严格区分大小写。
可以为类设置别名,以供其他地方引用,例如 上例中的 as t。
as 关键字是可选的, 一般别名推荐小写。
from前也可以加select 但必须配合别名使用。
HQL非常的强大与复杂。实际上,Hibernate的一个主要卖点就是查询语句的威力。下面我们会向大家一一介绍。
1.3 实体查询
根据上述HQL的查询步骤,编写一个最简单的查询,代码如示例4.2所示。
示例4.2
-
String hql = “from Thread”;
-
-
Query query = session.createQuery(hql);
-
-
List< Thread> List = query. list ( );
-
-
for ( Thread t : List ) {
-
-
System.out.println("名称:”+t getTitle());
-
-
}
上述代码中,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
-
@Test
-
-
public void hqlQuery2() throws Exception {
-
-
String hql="from Thread as t where t.title like '%鬼吹灯%' and t.dateCreated>'2010-07-10'";
-
-
Query query = session.createQuery(hql);
-
-
List<Thread> list = query.list();
-
-
for(Thread t : list){
-
-
System.out.println("帖子名称:"+t.getTitle()+
-
-
" 内容:"+t.getContent());
-
-
}
-
-
}
这些操作符的使用和SQL都没什么区别,只需要把握HQL面向对象的精神即可,这里不再赘述。
1.4 属性查询
现在我们使用HQL可以轻松的查询到我们需要的对象,但在某些情况下,我们并不需要取得完整的实体对象,如在下拉列表中显示出版块的名称,或者在加载帖子列表的时候,我们只需要帖子的标题等信息 而例如content这样比较大的属性并不需要加载,此时我们需要的数据可能仅仅是对象的某一个或者某几个属性,通过HQL我们可以轻松的做到这一点!代码如示例4.4所示。
示例4.4
-
@Test
-
-
public void hqlQuery3() throws Exception {
-
-
String hql="select t.title from Thread as t";
-
-
Query query = session.createQuery(hql);
-
-
List<String> list= query.list();
-
-
for(String title: list){
-
-
System.out.println("帖子标题:"+title);
-
-
}
-
-
}
单一属性查询,返回结果集属性列表,元素类型和实体类中相应的属性类型一致,但如果是对多个属性的查询HQL查询返回的结果又是什么呢?,多个属性的查询如示例4.5所示。
示例4.5
-
@Test
-
-
public void hqlQuery3() throws Exception {
-
-
String hql="select t.title,t.dateCreated from Thread as t";
-
-
Query query = session.createQuery(hql);
-
-
List<Object[]> list = query.list();
-
-
for(Object[] objects: list){
-
-
System.out.println("帖子标题:"+objects[0]+" 发布时间:"+objects[1]);
-
-
}
-
-
}
多个属性查询,返回的集合元素是对象数组,数组元素的类型和对应的属性在实体类中的类型一致,数组的长度取决于select中属性的个数。
另外还有一种更加面向对象化的属性查询方式 如示例4.6所示。
示例4.6
-
@Test
-
-
public void hqlQuery5() throws Exception {
-
-
String hql="select new Thread(th.title,th.hit) from Thread as th";
-
-
Query query = session.createQuery(hql);
-
-
List<Thread> list = query.list();
-
-
for(Thread t: list){
-
-
System.out.println("标题:"+t.getTitle()+
-
-
" 点击量:"+t.getHit());
-
-
}
-
-
}
这种方式能够在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
-
@Test
-
-
public void hqlQuery6() throws Exception {
-
-
String hql="from Thread as th where th.topped=? and th.title like ?";
-
-
Query query = session.createQuery(hql);
-
-
query.setBoolean(0,true);
-
-
query.setString(1, "%鬼吹灯%");
-
-
List<Thread> list = query.list();
-
-
for(Thread th: list){
-
-
System.out.println("标题:"+th.getTitle()+
-
-
" 置顶:"+th.getTopped());
-
-
}
-
-
}
使用这种方式绑定参数需要注意如下几点:
必须按照参数的顺序,调用相应的setType()方法赋值。
参数的下标从0开始。
如果有多个参数的时候,必须保证每个参数都被绑定值。
4.5.2 按照名称绑定
代码如示例4.8所示。
示例4.8
-
@Test
-
-
public void hqlQuery7() throws Exception {
-
-
String hql="from Thread as th where th.topped=:top and th.title like :name";
-
-
Query query = session.createQuery(hql);
-
-
query.setBoolean("top",true);
-
-
query.setString("name","%鬼吹灯%");
-
-
List<Thread> list = query.list();
-
-
for(Thread th: list){
-
-
System.out.println("标题:"+th.getTitle()+
-
-
" 置顶:"+th.getTopped());
-
-
}
-
-
}
使用按照名称进行绑定,请注意以下几点:
在HQL查询语句中定义命名参数时以 “:”开头。
Query提供的方法能绑定各种类型的参数。此类 setXXX()方法中,第一个参数用于设置各种类型的命名参数,第二个参数表示命名参数的值。
这种方式有比较好的可读性,可以避免因大意而产生的参数顺序错误。
4.5.3 封装参数
当需要绑定的参数非常多,那么无论使用按位置,还是按名称绑定参数都会非常的繁琐,HQL中提供了第三种方式进行参数的绑定。即先将参数值封装到bean中,然后将bean与Query进行绑定。代码如示例4.9所示。
示例4.9
-
@Test
-
-
public void hqlQuery8() throws Exception {
-
-
String hql="from Thread as th where th.title like :title and "+
-
-
"th.hit between:low_hit and :high_hit and th.replyCount>:rcount and th.topped=:top";
-
-
Query query = session.createQuery(hql);
-
-
-
-
Map queryProperties = new HashMap();
-
-
queryProperties.put("title","%java%");
-
-
queryProperties.put("high_hit",100000);
-
-
queryProperties.put("low_hit",10);
-
-
queryProperties.put("rcount",10);
-
-
queryProperties.put("top",false);
-
-
-
-
query.setProperties(queryProperties);
-
-
-
-
List<Thread> list = query.list();
-
-
for(Thread th: list){
-
-
System.out.println("标题:"+th.getTitle()+
-
-
" 点击量:"+th.getHit()+" 回帖:"+th.getReplyCount());
-
-
}
-
-
}
这种方式一定要注意封装参数值的bean中的属性名一定要和HQL语句中的命名参数名称一致,这样Hibernate才能根据名称进行匹配。
1.6 使用聚合函数
HQL中聚合函数的使用和SQL中基本一致。
1.6.1 count()
查询指定用户所发布的帖子总数,代码如示例4.10所示。
示例4.10
-
@Test
-
-
public void hqlQuery9() throws Exception {
-
-
String hql="select count(th) from Thread as th where th.author.id=1";
-
-
Query query = session.createQuery(hql);
-
-
Long count=(Long) query.uniqueResult();
-
-
System.out.println("用户发帖: "+count);
-
-
}
本例中使用了count()函数来计算总数,显然查询的结果不肯能有多个,这个时候再使用list方法求的集合,并从中取得数据,就显得很繁琐,这时候可以使用uniqueResult()来取得唯一结果,这里需要注意的是,如果返回的结果大于1个本方法将会抛出异常。
1.6.2 max()和 min()
求得所有帖子中最大,最小点击量。代码如示例4.11所示。
示例4.11
-
@Test
-
-
public void hqlQuery10() throws Exception {
-
-
String hql="select max(th.hit),min(th.hit)from Thread as th";
-
-
Query query = session.createQuery(hql);
-
-
List<Object[]> list = query.list();
-
-
Object[] object = list.get(0);
-
-
System.out.println("单贴最高点击: "+object[0]);
-
-
System.out.println("单贴最低点击: "+object[1]);
-
-
}
1.6.3 avg()和 sum()
求userid=1的用户所发布信息的总点击量和平均点击量,代码如示例4.12所示。
示例4.12
-
@Test
-
-
public void hqlQuery11() throws Exception {
-
-
String hql="select avg(th.hit),sum(th.hit)from Thread as th where th.author.id=1";
-
-
Query query = session.createQuery(hql);
-
-
List<Object[]> list = query.list();
-
-
Object[] object = list.get(0);
-
-
System.out.println("平均点击量: "+object[0]);
-
-
System.out.println("总点击量: "+object[1]);
-
-
}
-
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
-
@Test
-
-
public void hqlQuery12() throws Exception {
-
-
String hql = "select count (th) , th.type from Thread as th group by th.type";
-
-
Query query = session.createQuery(hql);
-
-
List< Object[]> list = query.list();
-
-
for(Object[] objects : list){
-
-
System.out.println( "数量:"+objects[0]+"类型: "+objects[1] );
-
-
}
-
-
}
having子句可以对group by子句进行甄选。例如,对帖子数量大于8的种类的分类统计,代码如示例4.14所示。
示例4.14
-
@Test
-
-
public void hqlQuery13() throws Exception {
-
-
String hql = "select count (th) , th.type from Thread as th group by th.type having count(th)>8";
-
-
Query query = session.createQuery(hql);
-
-
List< Object[]> list = query.list();
-
-
for(Object[] objects : list){
-
-
System.out.println( "数量:"+objects[0]+"类型: "+objects[1] );
-
-
}
-
-
}
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来检索对象。Session的get()和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
- 点赞
- 收藏
- 关注作者
评论(0)