序言
之前对hibernate中的查询总是搞混淆,不明白里面具体有哪些东西。就是因为缺少总结。在看这篇文章之前,你应该知道的是数据库的一些查询操作,多表查询等,如果不明白,可以先去看一下 MySQL数据表查询操作详解 ,以至于看这篇文章不用那么吃力。
--WH
一、hibernate中的5种检索方式
1.1、导航对象图检索方式
根据已经加载的对象导航到其他对象
例如:在前面的各种映射关系中,实体类包含对其他类对象的引用。
Dept d = (Dept) session.get(Dept.class,2);
d.getStaffSet().size(); //d对象关联Staff集合,hibernate会自动检索Staff数据。如何检索的,看下面图中发送的sql语句。
1.2、OID检索方式
按照对象的OID来检索对象
例如:session.get()/session.load()
这个大家度很熟悉了,就不用在这里过多的阐述了。
1.3、HQL检索方式
HQL:Hibernate Query Language ,是面向对象的查询语言,它和SQL查询语言有些相似,在Hibernate提供的各种检索方式中,HQL是使用的最广的一种检索方式,
注意:HQL操作的全是POJO类中的属性,而不是操作数据库表中的字段。
1.3.1、在查询语句中设定各种查询条件
1.3.2、支持投影查询,即仅检索出对象的部分属性
1.3.3、支持分页查询
1.3.4、支持连接查询
1.3.5、支持分组查询,允许使用HAVING 和 GROUP BY 关键字
1.3.6、提供内置聚集函数,如SUM(),MIN(),MAX()等
1.3.7、能够调用用户定义的SQL函数或标准的SQL函数
1.3.8、支持子查询
1.3.9、支持动态绑定参数
使用HQL检索步骤:
1>获得session
2>编写HQL
3>通过session.createQuery(HQL)创建Query对象
4>为Query对象设置条件参数(如果HQL中需要填充参数的话)
5>执行查询
list():返回一个集合列表,有可能集合中装的是数组,有可能是POJO对象。
uniqueResult():返回一个查询结果,在已知查询结果只有一个或者0个时,使用是没有问题的,如果返回结果有多个,那么就会报异常
实验
创建环境,使用Dept和Staff的双向一对多关系,其中具体的代码就不在列举出来了。重点不在这里。原始记录Staff中有9条记录,都指向了Dept中id为2的部门。
1.3.1、在查询语句中设定各种查询条件
1.3.1.1、查询全部记录,没有查询条件
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
结果
1.3.1.2、条件查询,查找出id=3的staff信息
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.2、支持投影查询,即仅检索出对象的部分属性
也就是不需要将表中所有的字段度查询出来,只查询出对象的部分属性,这就是投影查询
1.3.2.1、什么度不使用,直接查询,得到的结果就是将查到的属性全放到list集合中。这只使用于检索对象的一个属性,如果多个属性,就得需要用别的方式进行封装
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.2.2、使用new List()或者new Map()或new Staff()将返回的值给封装起来
使用new Staff()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
使用new List()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
使用new Map()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.3、支持分页查询
使用setFirst()和setMaxResult()分别设置起始索引和拿去数据的总数。跟limit m,n 是一样的
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.4、支持连接查询
支持7种连接写法。
1.3.4.1、内连接 inner join 可以省略inner,直接join
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.4.2、迫切内连接 inner join fetch
内连接返回的list中是Object[],而迫切内连接返回的list中是POJO类对象
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
要想得到不重复的记录,那么就将hql改为"select distinct d from Dept d inner join fetch d.staffSet";
1.3.4.3、隐式内连接 不写任何关键字,完成表连接
其实就是通过where连接两张表。很简单。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.4.4、左外连接,left outer join,可以省略outer,直接left join
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.4.5、迫切左外连接, left outer join fetch
一样的区别,就是list中存放的是POJO对象了
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.4.6、右外连接:right outer join
知道左外连接,右外连接也就会了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.4.7、交叉连接,会产生笛卡尔积。
什么是笛卡尔积?将两张表连接起来,比如一张表中有3条记录,另一张表中也有3条记录,那么连接之后,就会出现9条数据,其中就有一些重复的数据,拿实例说话,班级和学生,有三个学生A,B,C,有两个班级E,F,连接起来后,就会出现笛卡尔积,6条数据,其中会出现这样的数据,A,E 、A,F、B,E、B,F、C,E、C,F, A,B,C就重复出现了,即在E班级,又在F班级,这样就不合理。这就是所说的笛卡尔积。
由于dept中就一条记录,无法展示出笛卡尔积,所以手动增加一条dept的记录。然后在进行交叉连接。就会出现笛卡尔积,
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
注意:上面的连接中很多是使用一个引用就代表了相对应的表,比如" from Dept d inner join d.staffSet " d.staffSet就好像代表了Staff的这张表,实际上就是代表了Staff这张表,这样理解,from Dept 就从Dept表中找出了所有记录,就打比方,找到了Dept表中的2部门,在2部门中有多少staff呢,可以全部找出来,在我们所说的环境下,正好就staff就全部在该部门中,那么就找到了Staff表中的所有staff,也就是相当于是Staff这张表了,就算2部门没有包括所有的staff,那么还有其他部门,肯定包括了剩下的staff,也就是说,不管怎么样,度能把staff全部找到,所以d.staffSet就相当于Staff表了。以此类推,其他hql语句中的这里也是这样理解的。
1.3.5、支持分组查询,允许使用HAVING 和 GROUP BY 关键字
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.6、提供内置聚集函数,如SUM(),MIN(),MAX()等
这个例子中不好使用这几个函数。。。所以这里不演示了,很简单。hql和sql差不太多。
1.3.7、能够调用用户定义的SQL函数或标准的SQL函数
这个跟上面的一样,SQL中函数有很多。也就是说hql能够使用sql中的函数
1.3.8、支持子查询
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
1.3.9、条件查询,给hql中动态设置参数的两种方式
方式一:from Staff where id = ? 使用?代替所需要填入的值,在下面设置值时则从0开始算起,第一个?是处于0的位置,如果有两个?号,则使用0,1索引号来插入值。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
方式二:from Staff where id = :id 使用":id"这个名字来表示插入值的名称,在下面则不用索引号来确定插入值的位置,直接是使用这个别称
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
注意,在位置上不仅仅可以插入值,还可以插入一个对象。例如
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
这两种方式度可以,不过在参数比较多的情况下,建议使用别名,那样更加清楚,不易出错,在少量参数时可以使用索引。
1.4、QBC检索方式
QBC:Query By Criteria,是一种更加面向对象的查询语言,提供的一系列QBC API来检索对象。
HQL所能做的事情,使用QBC也大多能做用,这个通过实例来看看QBC是如何使用的。
步骤:
1>获得session
2>session.createCriteria(Obejct.class); 创建criteria对象
3>使用criteria的API方法进行条件的增加。add(Restrictions.eq(属性名,值))
4>执行查询
list():返回一个集合列表,有可能集合中装的是数组,有可能是POJO对象。
uniqueResult():返回一个查询结果,在已知查询结果只有一个或者0个时,使用是没有问题的,如果返回结果有多个,那么就会报异常
例子一:使用QBC来对Staff进行查询
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
例子二:使用QBC来对Staff进行条件查询
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
例子三:QBC也能进行连接查询
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
给一张表来看看qbc增加的条件查询语句。
重点有一个离线Criteria对象的用法。
1、在web层封装查询条件到离线Criteria对象中,将其DetachedCriteria对象绑定到Thread上。
2、到dao层,就能通过Thread拿到该离线Criteria对象,然后创建session。将session给DetachedCriteria,就能够执行查询
代码:
WEB层
DetachedCriteria detachedCriteria =DetachedCriteria.forClass(Customer.class);
detachedCriteria.add(Restrictions.eq("name", "kitty"));
DAO层
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
// 将离线查询对象 关联到Session
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
Customer customer = (Customer) criteria.uniqueResult();
1.5、本地SQL检索方式
使用标准的SQL语句来编写。
步骤:
1>获得session
2>编写sql语句
3>session.createSQLQuery(sql);获取SQLQuey对象
4>给sql语句设置参数。
5>执行查询
list():返回一个集合列表,集合中装的是Object[]。
返回实体类对象集合,如果与实体类进行了绑定,也就是使用了addEntity(xxx.class)。
例子一:查询Staff的所有记录
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
例子二:查询Staff的所有记录,并且绑定实体。 addEntity。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
二、总结
以上就是我们说的5种检索,其中说的重点就是hql的用法,上面的例子全部写完了差不多就对hql有一定的了解。记住hql是对pojo类进行操作,而不是对数据库中的表。在使用连接查询时,可以使用QBC,因为更简单,只需要用createAlias()就能使用任何的连接。一般在开发中sql语句都会提取出来放到hbm中,例如
在hbm映射文件 (也可以用注解配置)
<!-- 这里可以定义命名查询 -->
<!-- 定义 HQL 语句 <query name=""></query> -->
<!-- 定义 SQL 语句 <sql-query name=""></sql-query> -->
<query name="findCustomerByName">
<![CDATA[from Customer where name = ?]]>
</query>
* 为hql语句 起了一个名字
程序代码:
//相当于把sql语句分离开来了。方便维护
Query query = session.getNamedQuery("findCustomerByName");
query.setParameter(0, "tom");
Customer customer = (Customer) query.uniqueResult();