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/descHQl检索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知识点复习之四
原文地址:http://blog.51cto.com/14008076/2303718