Hibernate知识点复习之四
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate知识点复习之四相关的知识,希望对你有一定的参考价值。
Hibernate知识点复习之四
Hibernate的检索
检索方式分类:对象图导航检索方式,OID检索方式,HQL检索方式,QBC检索方式,SQL检索方式
1 对象图导航检索方式:
根据已加载的对象,导航到他的关联对象,它是利用类与类的关系来检索对象,如要查找一个联系人对应的客户,
就可以由联系人对象自动导航找到联系人所属的客户对象,前提是必须在映射文件中配置多对一的关系,其检索方式如下:
LinkMan linkMan=(LinkMan)session.get(Customer.class,1L);
Customer customer=linkMan.getCustomer();
2 OID检索方式
概述:OID检索方式主要指用Session的get()和load()方法加载某条记录对应的对象,方式如下:
Customer customer1 = session.get(Customer.class,3L);
Customer customer2 = session.load(Customer.class,4L);
底层深入分析get()和load()的区别:
(1)get不支持lazyd(懒加载/延迟加载),load支持lazy
lazy表示只有在用到的时候才加载数据,
如:Student student = (Student)session.load(Student.class,1); //不会发出SQL语句
student.getName(); //这条语句才会发出SQL语句
而使用get方法,Student student = (Student)session.get(Student.class,1); //会发出SQL语句
(2) 采用get加载数据,如果数据库中不存在相应的数据,那么返回null;
采用load加载数据,如果数据库中不存在相应的数据,那么抛出ObjectNotFoundException
(3)load方法可返回实体的代理类实例,而get方法永远直接返回实体类。
(4) load方法可以充分利用内部缓存和二级缓存中的现有数据
get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。
Session在加载实体对象时,将经过的过程:
首先,Hibernate中维持了两级缓存。第一级缓存由Session实例维护,其中保持了Session当前所有关联实体的数据,也称为内部缓存。
而第二级缓存则存在于SessionFactory层次,由当前所有由本SessionFactory构造的Session实例共享。
出于性能考虑,避免无谓的数据库访问,Session在调用数据库查询功能之前,会先在缓存中进行查询。
首先在第一级缓存中,通过实体类型和id进行查找,如果第一级缓存查找命中,且数据状态合法,则直接返回。
之后,Session会在当前“NonExists”记录中进行查找,如果“NonExists”记录中存在同样的查询条件,则返回null。
“NonExists”记录了当前Session实例在之前所有查询操作中,未能查询到有效数据的查询条件(相当于一个查询黑名单列表)。
如此一来,如果Session中一个无效的查询条件重复出现,即可迅速作出判断,从而获得最佳的性能表现。
对于load方法而言
如果内部缓存中未发现有效数据,则查询第二级缓存,如果第二级缓存命中,则返回。
如在缓存中未发现有效数据,则发起数据库查询操作(Select SQL),如经过查询未发现对应记录,
则将此次查询的信息在“NonExists”中加以记录,并返回null。
根据映射配置和Select SQL得到的ResultSet,创建对应的数据对象。
将其数据对象纳入当前Session实体管理容器(一级缓存)。
执行Interceptor.onLoad方法(如果有对应的Interceptor)。
将数据对象纳入二级缓存。
如果数据对象实现了LifeCycle接口,则调用数据对象的onLoad方法。
返回数据对象。
3 HQL检索方式(Hibernate官方推荐的查询语言):
概述:HQL是面对对象的查询语言,它和SQL查询语言相似,但它使用的是类,对象和属性概念,没有表和字段概念。
功能:
* 在查询语句中设定查询条件
* 支持动态绑定参数
* 支持分组查询,允许使用group by和having关键字
* 提供内置的聚合函数,如sum()和min(),max()
* 支持子查询,即嵌套查询
* 支持分页查询
* 支持投影查询,即仅检索出对象的部分属性
* 能够调用用户定义的sql函数
完整的hql语句结构:
select ...from...where...group by ...having... order by ...asc/desc
HQl检索API示例:
1 hql基本检索
String hql="from Customer";
//String hql="select * from Customer";此句Hibernate无法识别*通配符
//String hql="select c from Customer c;使用select 需要为Customer起别名
Query query = session.createQuery(hql);
List<Customer> list = query.list();
2 排序检索
String hql="from Customer order by cust_id desc";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
3 条件查询:
按位置绑定参数
String hql1="from Customer where cust_id = ?";
Query query1 = session.createQuery(hql1);
query1.setParameter(0,9L);
Customer c1 = (Customer) query1.uniqueResult();
按名称绑定参数
String hql2="from Customer where cust_id =:myId";
Query query2 = session.createQuery(hql2);
query2.setParameter("myId",8L);
Customer c2 = (Customer) query2.uniqueResult();
4 分页检索
String hql="from Customer";
Query query = session.createQuery(hql);
//设置分页
query.setFirstResult(0);
query.setMaxResults(5);
List<Customer> list = query.list();
5 统计检索:count()总记录,sum()算术总和,avg()算术平均值,min()算术最小值,max()算术最大值
String hql="select count(*) from Customer";
String hql3="select sum(cust_id) from Customer";
String hql4="select avg(cust_id) from Customer";
String hql5="select max(cust_id) from Customer";
String hql6="select min(cust_id) from Customer";
6 投影查询--也就是查询对象的某一个属性
查询单个属性
String hql="select cust_name from cn.itheima.domain.Customer"; //所有Customer的cust_name属性
查询多个属性
String hql2="select cust_name,cust_id from cn.itheima.domain.Customer";
7 投影的构造方式查询:
结果识别为Customer实体的属性
但前提是:Customer实体中一定要存在 相应的构造方法(空参和实参都要有)
String hql3="select new Customer(cust_id,cust_name) from cn.itheima.domain.Customer";
4 QBC检索:
1 概述:QBC(Query By Criteria)它由Criteria接口,Criterion接口,Expression类组成。
Criteria接口是HibernateAPI的一个查询接口,它由Session进行创建。
Criterion是Criteria的查询条件,在Criteria中提供add(Criterion criterion)方法来添加查询条件
而Criterion查询条件的创建是通过Restrictions工具类的静态方法:
Restrictions.eq 等于
Restrictions.allEq 对象是Map,使用key/value进行多个等于比较
Restrictions.gt 大于>
Restrictions.ge 大于等于>=
Restrictions.lt 小于
Restrictions.le 小于等于
Restrictions.between 对应sql的between子句
Restrictions.like 对应sql的like子句
Restrictions.in 对应sql的in子句
Restrictions.and and关系
Restrictions.or or关系
Restrictions.sqlRestriction sql限定查询
2 QBC检索API示例:
(1)QBC基本查询
//创建Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);//相当于select * from customer
//获取查询结果
List list = criteria.list();
(2)Criteria条件查询
//创建Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
//设置查询条件
//Criterion criterion2 = Restrictions.idEq(2l);
Criterion criterion = Restrictions.eq("cust_id",2l);
//把查询条件criterion添加进Criteria对象中
criteria.add(criterion);
//获取查询结果
List list = criteria.list();
(3)Criteria的分页查询
创建Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
设置分页参数
criteria.setFirstResult(0);
criteria.setMaxResults(1);
List list = criteria.list();
(4)Criteria的排序查询
获取Criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
排序
//criteria.addOrder(Order.asc("cust_id")); //升序
criteria.addOrder(Order.desc("cust_id")); //降序
List list = criteria.list();
(5)criteria的统计
criteria.setProjection(Projections.rowCount());
//criteria.setProjection(Projections.max("cust_id"));
//criteria.setProjection(Projections.min("cust_id"));
//criteria.setProjection(Projections.avg("cust_id"));
//criteria.setProjection(Projections.sum("cust_id"));
List list = criteria.list();
3 离线的QBC条件查询DetachedCriteria(SSH整合后经常用)
概述:DetachedCriteria是一种脱离Session来使用的条件查询对象(传统的Criteria对象必须由Session创建)
其不受限于session,易于在不同架构层中封装数据进行传递
API:
离线DetachedCriteria的创建
DetachedCriteria dc=DetachedCriteria.forClass(Xxx.class);
离线DetachedCriteria转换为正常Criteria,接收Session
Criteria criteria =dc.getExecutableCriteria(session);
示例:
模拟此处在service/web层
//创建离线DetachedCriteria
DetachedCriteria dtCriteria = DetachedCriteria.forClass(Customer.class);
//进行条件 id查询(和普通的查询方式一样)
dtCriteria.add(Restrictions.idEq(2l));
模拟此处在dao层
//获取session对象
Session session = HibernateUtils.openSession();
//开启事务
Transaction tx = session.beginTransaction();
//操作-------------------------
//离线DetachedCriteria传递到 dao层,接收session变成正常的 Criteria查询对象
Criteria criteria = dtCriteria.getExecutableCriteria(session);
List list = criteria.list();
//事务提交,资源释放------------------------
tx.commit();
session.close();
5 Hibernate本地SQL检索方式:
概述:采用HQL或QBC检索方式时,Hiberante底层会生成标准的SQL查询语句,适用于所有数据库平台,它们是跨平台的。
但有的应用程序需根据底层数据库sql方言来生成一些特殊查询语句,这就需用到Hibernate提供的SQL检索方式。
本地SQL检索的示例代码:
SQLQuery sqlQuery=session.createSQLQuery("xxxx");
6 Hibernate的多表查询
(1)原生的SQL多表查询:
& 连接查询:
<1>交叉连接(开发一般不使用)
概述:返回的结果是被连接的两个表中所有数据行的笛卡尔积,如A表有10条数据,B表有8条数据,返回的结果就是10*8。
语法格式
格式一:select * from 表1 cross join 表2;
格式二:select * from 表1,表2;
<2>内连接(又称简单连接或自然连接)
概述:内连接使用比较运算符对两个表的数据进行比较,并列出与连接条件相匹配的数据行,组合成新的记录。
语法格式:select 查询字段 from 表1 [inner] join 表2 on 连接条件
内连接可再细分两类
隐式内连接(隐式就是看不见inner join关键字,用where关键字代替)
select * from 表1,表2 where 表1.关系字段=表2.关系字段
显示内连接(存在inner join关键字)
select * from 表1 inner join 表2 on 表1.关系字段=表2.关系字段
<3>外连接
概述:返回的结果不仅包含符合查询条件的数据,而且还包含左表(左外连接),右表(右外连接),或两个表(全外连接)中所有数据
外连接完整格式:
select * from 表1 left | right outer join 表2 on 表1.关系字段=表2.关系字段 where 条件
左连接:返回左表中所有记录和右表中符合连接条件的记录
select * from 表1 left outer join 表2 on 表1.关系字段=表2.关系字段 where 条件
右连接:返回右表中所有记录和左表中符合连接条件的记录
select * from 表1 right outer join 表2 on 表1.关系字段=表2.关系字段 where 条件
(2)HQL连接查询:
& 交叉查询
& 内连接
显示内连接
隐式内连接
迫切内连接
& 外连接
左外连接
右外连接
迫切外连接
&查询语法:
* 概述:HQL连接查询语法和原生的SQL语法差别不大,区别在于HQL连接查询作用的是对象而不是表
* SQL和HQL内连接示例比较
SQL显示内连接:
select * from cst_customer c inner join cst_linkman l on c.cust_id = l.lkm_id
HQL内连接:
from Customer c inner join c.linkMans
* HQL的连接不用写关联字段,不用写具体的on条件,直接写关联属性即可。
* 迫切内连接(在内连接 inner join 后添加一个fetch关键字。)
fetch关键字分析:我们会发现无论是内连接还是迫切内连接生成的sql语句是一样的,fetch关键字不会出现在sql语句中,
因为sql语句没有fetch关键字,fetch只能在hql中使用,生成sql后就消失。
fetch的作用:
普通内连接封装数据时,会将属于客户的数据封装到Customer实体中,会将属于联系人的数据封装到LinkMan实体中
所以封装后获得的数据为List<Object[]{Customer,LinkMan}>
示例:
String hql="from Customer c inner join c.linkMans";
//返回的List集合存储着一个数组,存储着客户和联系人两个对象 Object[]{Customer,LinkMan}
List<Object[]> list = session.createQuery(hql).list();
for (Object[] objects : list) {
Customer c= (Customer) objects[0];
LinkMan l=(LinkMan) objects[1];
System.out.println(c);
System.out.println(l);
}
迫切内连接封装数据时,会将属于客户的数据封装到Customer实体中,会将属于联系人的数据封装到Customer实体中的联系人集合里
所以封装后获得的数据为List<Customer>,但要注意的是,迫切内连接会出现重复的记录,这就需我们用到 distinct关键字进行解决
示例:
String hql2="select distinct c from Customer c inner join fetch c.linkMans";
List<Customer> list2 = session.createQuery(hql2).list();
for (Customer customer : list2) {
System.out.println(customer);
}
* Hibernate的内连接和迫切内连接总结:
两者主要区别在于封装数据的方式,但查询的结果集是一样的,底层生成的sql也一样
Hibernate的查询优化
概述:在很多CRM案例中要用到查询操作,但Hibernate本身的查询效率不是很好,特别在获取关联对象的方面,我们需要对查询语句进行一些优化
Hibernate的抓取策略
1 抓取策略概述:Hibernate的抓取策略是一种提升Hibernate性能的手段,在Hibernate获取关联对象时,对发送语句进行优化,需使用到延迟加载。
2 延迟加载(lazy load 懒加载)概述:Hibernate使用load方法关联对象默认的加载方式,所谓的延迟加载就是当真正需要数据时,才真正执行数据加载操作。
3 延迟加载分类
<1> 类级别延迟:
概述:类级别延迟指的是查询某个对象时,是否采用延迟,通常在<class>标签上配置lazy属性
分析:& Hibernate查询某个对象时,默认选择类级别延迟(<class lazy="true">),所以使用load方法检索某个对象时,
不会马上发送sql语句,真正调用调用该对象时才会发送sql语句
& 若不想使用延迟加载,可以直接在映射文件上设置<class lazy="false">,也可以用final修饰持久类,
使之无法生成代理类, 就会使延迟加载失效。
& 值得一提的是,类级别的延迟加载我们一般不进行修改,采用默认值 lazy="true"
<2> 关联级别延迟
概述:关联级别延迟指的是,查询一个对象的关联对象时是否采用延迟加载,这个通常在<set>或<many-to-one>上配置lazy属性
分析:
& <set>标签上的lazy取值
% true:默认值,采用延迟加载
% false:检索关联对象的时候,不采用延迟加载
% extra:极其懒惰
&<many-to-one>标签上的lazy取值
% proxy:默认值,是否采用延迟取决于一方上的延迟加载lazy值
% false:检索关联对象时,不采用延迟加载
% no-proxy:不用研究
<3>延迟加载总结:
* 延迟加载:仅仅获得没有使用,不进行查询,使用才进行查询
* 是否对类进行延迟加载:可以通过在实体类的class元素配置lazy属性来控制
* lazy配置只作用于 load方法,执行该方法时,不发送任何sql语句,只返回一个对象,使用该对象才执行查询
* lazy="true"--进行延迟加载:加载时不执行,使用时才查询
* lazy="false"--不进行延迟加载:加载时立即执行
* 延迟加载的原理是:
* 它会把获取到的Customer实体对象变成一个 具有加强功能的超级代理类型对象 Customer_$$
* (增强的功能就是:使用这个对象才执行查询,若不使用则不执行查询)
* 值得注意的是:使用懒加载调用属性时要确保 session还在打开,因为它生成的代理对象是根据关联的 session
* 查询数据库的,否则会报异常
* 结论:建议使用延迟加载,因为它可以把节省不必要浪费的资源,提高效率
3 抓取策略指的是查询某个对象时,如何抓取其关联对象,也可通过配置实现:
* <set>标签上的fetch取值:
& select :默认值,发送的是普通的select语句
& join:发送一条迫切左外连接去查询,值得一提的是,set上设置了fetch=join,lazy就会失效。
& subselect:发送一条子查询语句查询其关联对象
* <many-to-one>标签上的fetch有两个取值
& select :默认值,发送的是普通的select语句
& join:发送一条迫切左外连接去查询
4 抓取策略fetch和延迟加载lazy配合使用分析:
<1>单表查询加载:fetch="select"
延迟加载:lazy="true"
<2>单表查询加载:fetch="select"
立即加载:lazy="false"
<3>单表查询加载:fetch="select"
极其懒加载:lazy="extra",与懒加载效果基本一致,如果想获得集合size(),则仅用count语句进行查询
<4>多表查询加载:fetch="join" 一次性查询所有的数据,使懒加载失效
延迟加载:lazy="true"
<5>子查询:fetch="subselect" 主要用于重复繁杂查询数据过程中,否则和 select效果一样
延迟加载:lazy="true"
<5>子查询:fetch="subselect" 主要用于重复繁杂查询数据过程中,否则和 select效果一样
立即加载:lazy="false"
5 <set>集合上的fetch和lazy特点总结
(1)fetch:控制的是 查询其关联对象时采用的sql语句格式
* select:默认值,发送一条select语句查询其关联对象
* join:发送一条迫切左外连接查询其关联对象
* subselect:发送一条子查询查询其关联对象
(2)lazy:控制的是 查询其关联对象时采用的延迟加载策略
* true:延迟加载
* false:立即加载
* extra:极其懒惰
6 批量抓取(同时查询多个对象的关联对象)
(1)实现批量查询一方关联的多方:
<set>标签内配置 batch-size属性,如:
<!--批量抓取 查询一次,抓取集合数量为3
抓取客户的集合时,一次抓取多个客户联系人的集合
-->
<set name="linkMans" batch-size="3" >
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan"/>
</set>
(2)实现批量查询多方关联的一方:
在多方的<class>标签中配置 batch-size即可
注意,并不是在<many-to-one>标签内配置
以上是关于Hibernate知识点复习之四的主要内容,如果未能解决你的问题,请参考以下文章