Hibernate检索策略

我会带着你远行 2022-07-14 03:00 252阅读 0赞

Hibernate检索策略

  1. 它是为了让我们的查询更加的有效率,是优化我们查询用的。 它是靠配置的方式来实现的

延迟加载

  1. 延迟加载(lazy load)是(也称为懒加载 )Hibernate关联关系对象默认的加载方式,延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。
  2. 通常将延迟加载分为两类 :一类叫做类级别延迟 ,另一类叫做关联级别的延迟。类级别的延迟指的是查询某个对象的时候,是否采用有延迟,这个通常在<class>标签上配置lazy属性。关联级别的延迟指的是,查询一个对象的关联对象的时候是否采用延迟加载。这个通 常在<set>或<many-to-one>上配置lazy属性。

类级别的延迟加载

  1. 使用load方法检索某个对象的时候,这个类是否采用延迟加载的策略,就是类级别的延迟。类级别的延迟一般在<class>上配置lazy属性,lazy的默认值是 true 。默认是延迟加载的 ,所以使用 load方法去查询的时候 ,不会马上发送SQL语句。当真正使用该对象的时候,才会发送SQL语句。
  2. Customer customer = session.load(Customer.class,1L);
  3. 其实如果不想使用延迟加载也有很多种方法,当然最简单的就是将这个类的映射文件上的lazy设置为false,当然也可以将这个持久化类改为final修饰,如果改为final修饰的话。就无法生成代理类,就会使延迟加载失效。
  4. 这是类级别的延迟加载, 类级别的延迟加载一般不进行修改 ,采用默认值 lazy="true"就可以了。

关联级别的延迟加载

  1. 关键级别的延迟加载指的是查询到某个对象以后,检索它的关联对象的时候是否采用延迟加载
  2. Customer customer = session.get(Customer.class,1L);
  3. Set<LinkMan> linkMans = customer.getLinkMans();
  4. 通过客户查询其关联的联系人对象,在查询联系人的时候是否采用延迟加载称为是关联级别的延迟。关联级别的延迟通常是在<set>和<many-to-one>上来进行配置

标签上的lazy通常有三个取值 :

  • true : 默认值,采用延迟加载
  • false: 检索关联对象 的时候,不采用延迟加载 。
  • extra : 极其懒惰的,用到什么加载什么。

标签上的 lazy 通常有三个取值:

  • proxy:默认值,是否采用延迟取决于一的一方类上的lazy属性的值 。
  • false:检索关联对象的时候,不采用延迟加载。
  • no-proxy: 不用研究。

关联级别的抓取策略

  1. 抓取策略指的是查询到某个对象的时候,如何抓取其关联对象。这个也可以通过配置完成 。在关联对象的标签上配置fetch属性。关联上就分为是在<set>和<many-to-one>上,也都有不同的取值

标签上的fetch通常有三个取值:

  • select:默认值,发送的是普通的 select 语句查询。
  • join:发送一条迫切左外连接去查询。
  • subselect:发送一条子查询语句查询其关联对象。

标签上的 fetch 有两个取值 :

  • select:默认值,发送一条普通的select 语句查询关联对象。
  • join:发送一条迫切左外连接语句查询其关联对象。
  1. set上配置fetch有三个值,lazy有三个值,这样就会产生很多种的效果。其实不用担心,因为fetch如果设置为joinlazy就会失效了。
  2. 简单的总结一 fetchlazy的作用,其实fetch主要控制抓取关联对象的时候的发送SQL语句的格式的。lazy主要控制查询其关联对象的时候是否采用延迟加载的。

批量抓取

  1. 在抓取的策略中有一种叫做批量抓取,就是同时查询多个对象的关联对象的时候,可以采用批量抓取进行优化 。当然这不是特别的重要。如果要实现批量的抓取效果,可以通过配置batch-size来完成。
  2. 可以在classset上配置
  • 在一的那方的class配置,表示在查询多方时可以同时获取几个一方的对象。
  • 在一的那方的set上配置,表示在查询一方时可以同时获取几个多方的对象。

实际开发中推荐的取值

  1. 一对多(多对多)的情况:
  2. lazytrue
  3. fetchselect
  4. 多对一的情况:
  5. lazyfalse
  6. fetchselect

一对多(多对多) 情况






















































序号


lazy的取值


fetch的取值


说明(都是以客户订单的一对多关系为例)


1


true(默认值)


select(默认值)


时机:用时才真正去查询订单。


语句形式:有1条查询客户的和多条查询订单的select语句。


batch-size:设置批量检索的深度。(建议3~10之间)


2


false


select(默认值)


时机:不管用不用订单,查询客户时都立即查询订单。


语句形式:有1条查询客户的和多条查询订单的select语句。


batch-size:设置批量检索的深度。(建议3~10之间)


3


extra


select(默认值)


时机:用什么信息,查什么信息。只查询必要的。


语句形式:有1条查询客户的和多条查询订单的select语句。


batch-size:设置批量检索的深度。(建议3~10之间)


4


true


subselect


时机:用时才真正去查询订单。


语句形式:子查询


batch-size:无效


5


false


subselect


时机:不管用不用订单,查询客户时都立即查询订单。


语句形式:子查询


batch-size:无效


6


extra


subselect


时机:用什么信息,查什么信息。只查询必要的。


语句形式:子查询


batch-size:无效


 7




true|false|extra


join(当join有效时,根本不看lazy属性)


时机:无效。因为连接查询,一次就是两张表及以上。


语句:left outer join


batch-size:无效


注意:Query查询会忽略join的存在。当join无效时,lazy就有效了。

多对一(一对一) 情况






























序号


lazy的取值


fetch的取值


说明(都是以客户订单的一对多关系为例)


1


proxy


select


时机:


    对象类级别的策略:true。


延迟加载。得到的是代理对象。


    对象类级别的策略:false。


立即加载。得到就是类对象。


语句:


    多条SQL语句


2


false


select


时机:立即加载。与对象类级别的策略无关。


语句:多条SQL语句。


3


true|false|extra


join(当join有效时,根本不看lazy属性)


时机:无效。因为连接查询,一次就是两张表及以上。


语句:left outer join


batch-size:无效


注意:Query查询会忽略join的存在。当join无效时,lazy就有效了。

测试Hibernate检索策略

  1. package com.pc.hibernate.test.querystrategy;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.hibernate.Transaction;
  6. import org.junit.Test;
  7. import com.pc.hibernate.domain.Customer;
  8. import com.pc.hibernate.domain.LinkMan;
  9. import com.pc.hibernate.utils.HibernateUtils;
  10. /**
  11. * Hibernate的检索策略
  12. * 明确:
  13. * 它是为了让我们的查询更加的有效率,是优化我们查询用的。
  14. * 它是靠配置的方式来实现的。
  15. * 关联级别的检索策略关心的是:
  16. * 实体类中的那个关联对象。
  17. * 例如:
  18. * 客户实体类中,关心的是联系人
  19. * 联系人实体类中,关心的是客户
  20. * 用户实体类中,关心的是角色
  21. * 角色实体类中,关心的是用户
  22. * 类级别检索策略关心的是:
  23. * 是当前实体类中所对应数据库表中所包含字段。
  24. * 类级别解决的问题:
  25. * 什么时候真正去查询,查询实体类中除了OID外的其他字段信息。
  26. *
  27. * 关联级别的检索策略解决的问题:(从哪些方面优化)
  28. * 1、查询的时机:
  29. * 到底什么时候真正的去查询
  30. * 立即加载
  31. * 延迟加载:惰性加载,懒加载
  32. * 2、采用什么方式去查询
  33. * 多条SQL语句
  34. * 子查询
  35. * 表连接
  36. * 关联级别的检索策略分几种情况:
  37. * 分为两种情况:
  38. * 一对多和多对多:
  39. * 有少的一方,根据少的一方获取多的一方。
  40. * 多对一和一对一
  41. * 有多的一方,根据多的一方获取少的一方。
  42. * 关联级别涉及的配置
  43. * 一对多(多对多)情况:
  44. * 涉及的标签:set
  45. * 涉及的属性:lazy:什么时候去查询
  46. * 取值:
  47. * true:延迟加载(默认值)
  48. * false:立即加载
  49. * extra:极懒加载 用什么数据,查什么数据,不用的都不查
  50. * fetch:用什么方式去查询
  51. * 取值:
  52. * select:多条SQL语句(默认值)
  53. * subselect:子查询
  54. * join:表连接
  55. * 多对一(一对一)情况:
  56. * 涉及的标签:many-to-one
  57. * 涉及的属性:lazy:什么时候去查询
  58. * false:立即加载
  59. * proxy:看关联对象的类级别检索策略
  60. * no-proxy:不用管,实际开发根本不用
  61. * fetch:用什么方式去查询
  62. * select:多条SQL语句
  63. * join:表连接
  64. *
  65. *
  66. * 实际开发中:
  67. * 一对多(多对多)的情况:
  68. * lazy:用true
  69. * fetch:用select
  70. * 多对一的情况:
  71. * lazy:false
  72. * fetch:select
  73. *
  74. * @author Switch
  75. *
  76. */
  77. public class TestQueryStrategy1 {
  78. /**
  79. * 需求:
  80. * 查询id为1的客户下的所有联系人
  81. * 查询的时机:
  82. * 延迟加载
  83. * 查询的方式:
  84. * 多条语句。2条,一条查询了客户,一条查询了联系人
  85. */
  86. @Test
  87. public void test1(){
  88. Session session = HibernateUtils.getCurrentSession();
  89. Transaction tx = session.beginTransaction();
  90. //1.查询id为1的客户
  91. Customer customer = session.get(Customer.class, 1L);
  92. //2.使用对象导航查询,查询该客户的联系人
  93. //类级别检索策略
  94. System.out.println(customer);
  95. System.out.println(customer.getLinkMans().size());
  96. //关联级别检索策略
  97. System.out.println(customer.getLinkMans());
  98. tx.commit();
  99. }
  100. /**
  101. * 需求:
  102. * 查询所有客户,获取每个客户的联系人
  103. * 查询的时机
  104. * 延迟加载
  105. * 查询的方式
  106. * 多条语句。5条SQL语句。查询联系人只有4条语句,还有一条是查询客户的。
  107. * Hibernate的N+1问题:
  108. * 当我们查询一个从表关联对象集合时,正常查询应该是N条语句,但是由于需要查询主表,所以会多出一条语句。
  109. * 解决N+1问题:
  110. * 在Hibernate的映射配置文件中,添加一个batch-size属性。该属性的含义:批量抓取。简单的说就是问号的个数。
  111. * batch-size属性的取值:建议3~10之间。
  112. */
  113. @Test
  114. public void test2(){
  115. Session session = HibernateUtils.getCurrentSession();
  116. Transaction tx = session.beginTransaction();
  117. //1.查询所有客户
  118. Query query = session.createQuery("from Customer");
  119. @SuppressWarnings("unchecked")
  120. List<Customer> customers = query.list();
  121. for (Customer customer : customers) {
  122. //2.使用对象导航查询,查询该客户的联系人
  123. //类级别检索策略
  124. System.out.println(customer);
  125. //关联级别检索策略
  126. System.out.println(customer.getLinkMans());
  127. }
  128. tx.commit();
  129. }
  130. /**
  131. * 需求:
  132. * 查询id为1的联系人的所属客户
  133. * 查询的时机:false
  134. * 立即加载
  135. * 查询的方式:select
  136. * 多条语句
  137. */
  138. @Test
  139. public void test3(){
  140. Session session = HibernateUtils.getCurrentSession();
  141. Transaction tx = session.beginTransaction();
  142. //1.查询id为1的联系人
  143. LinkMan l1 = session.get(LinkMan.class, 1L);
  144. //2.使用对象导航查询,获取该联系人的客户
  145. System.out.println(l1);
  146. System.out.println(l1.getCustomer());
  147. tx.commit();
  148. }
  149. /**
  150. * 需求:
  151. * 查询id为1的联系人的所属客户
  152. * 查询的时机:proxy
  153. * 看关联对象的类级别检索策略
  154. * 查询的方式:select
  155. * 多条语句
  156. */
  157. @Test
  158. public void test4(){
  159. Session session = HibernateUtils.getCurrentSession();
  160. Transaction tx = session.beginTransaction();
  161. //1.查询id为1的联系人
  162. LinkMan l1 = session.get(LinkMan.class, 1L);
  163. //2.使用对象导航查询,获取该联系人的客户
  164. System.out.println(l1);
  165. System.out.println(l1.getCustomer());
  166. tx.commit();
  167. }
  168. }

Customer.hbm.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  5. <!--
  6. 1、导入dtd约束
  7. 在Hibernate的核心jar包中:hibernate-mapping-3.0.dtd
  8. 2、编写配置文件
  9. 实体类和数据库表的对应关系
  10. 实体类中属性和表的字段的对应关系
  11. -->
  12. <hibernate-mapping package="com.pc.hibernate.domain">
  13. <!-- class标签:
  14. 作用:用于配置实体类和表之间的对应关系
  15. 属性:
  16. name:实体类的名称。它应该写全限定类名
  17. table:数据库表的名称
  18. -->
  19. <class name="Customer" table="cst_customer" lazy="true" batch-size="3">
  20. <!-- id标签:
  21. 作用:映射主键
  22. 属性:
  23. name:实体类的属性名称
  24. column:数据库表的字段名称
  25. type:主键的类型
  26. -->
  27. <id name="custId" column="cust_id" type="java.lang.Long">
  28. <!-- generator标签:
  29. 作用:主键的生成方式
  30. 属性:
  31. class:指定方式
  32. 取值:native
  33. 含义:使用本地数据库的自动增长能力。
  34. -->
  35. <generator class="native"/>
  36. </id>
  37. <!-- property标签:
  38. 作用:映射其他字段
  39. 属性:
  40. name:实体类的属性名称
  41. column:数据库表的字段名称
  42. type:字段的类型
  43. length:数据库中对应列的长度
  44. -->
  45. <property name="custName" column="cust_name" type="java.lang.String" length="32"/>
  46. <property name="custSource" column="cust_source" type="java.lang.String" length="32"/>
  47. <property name="custIndustry" column="cust_industry" type="java.lang.String" length="32"/>
  48. <property name="custLevel" column="cust_level" type="java.lang.String" length="32"/>
  49. <property name="custAddress" column="cust_address" type="java.lang.String" length="128"/>
  50. <property name="custPhone" column="cust_phone" type="java.lang.String" length="64"/>
  51. <!-- 配置一对多 -->
  52. <set name="linkMans" inverse="false" cascade="save-update" batch-size="4" lazy="true" fetch="select">
  53. <key column="lkm_cust_id" />
  54. <one-to-many class="LinkMan"/>
  55. </set>
  56. </class>
  57. </hibernate-mapping>

LinkMan.hbm.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping package="com.pc.hibernate.domain">
  6. <class name="LinkMan" table="cst_linkman">
  7. <id name="lkmId" column="lkm_id" type="java.lang.Long">
  8. <generator class="native" />
  9. </id>
  10. <property name="lkmName" column="lkm_name" type="java.lang.String" length="16"/>
  11. <property name="lkmGender" column="lkm_gender" type="java.lang.String" length="10"/>
  12. <property name="lkmPhone" column="lkm_phone" type="java.lang.String" length="16"/>
  13. <property name="lkmMobile" column="lkm_mobile" type="java.lang.String" length="16"/>
  14. <property name="lkmEmail" column="lkm_email" type="java.lang.String" length="64"/>
  15. <property name="lkmPosition" column="lkm_position" type="java.lang.String" length="16"/>
  16. <property name="lkmMemo" column="lkm_memo" type="java.lang.String" length="512"/>
  17. <!-- 配置多对一 -->
  18. <many-to-one name="customer" class="Customer" column="lkm_cust_id" cascade="save-update" lazy="proxy" fetch="select"/>
  19. </class>
  20. </hibernate-mapping>

hibernate.cfg.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE hibernate-configuration PUBLIC
  3. "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
  5. <!-- 1、导入dtd约束 在hibernate核心jar包中:hibernate-configuration-3.0.dtd 2、编写配置文件 -->
  6. <hibernate-configuration>
  7. <!-- 配置SessionFactory -->
  8. <!-- 创建SessionFactory需要3部分信息。 而配置文件中要写哪些配置,我们可以翻阅资料。 -->
  9. <!-- 第一部分:连接数据库的基本信息,第二部分:hibernate的基本配置,第三部分:映射文件的位置 -->
  10. <session-factory>
  11. <!-- 1、连接数据库的基本信息 -->
  12. <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  13. <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mycrm</property>
  14. <property name="hibernate.connection.username">root</property>
  15. <property name="hibernate.connection.password">123456</property>
  16. <!-- 数据库的方言 -->
  17. <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  18. <!-- 2、hibernate的基本配置 -->
  19. <!-- 是否显示SQL语句 -->
  20. <property name="hibernate.show_sql">true</property>
  21. <!-- 是否格式化显示SQL语句 -->
  22. <!-- <property name="hibernate.format_sql">true</property> -->
  23. <!-- 采用何种策略来创建表结构: -->
  24. <!-- update:检查表结构和实体类映射文件的变化,如果发现映射文件和表结构不一致,更新表结构。 -->
  25. <!-- 注意:hibernate不能创建数据库,只能在有数据库的情况下创建表结构。 -->
  26. <property name="hibernate.hbm2ddl.auto">update</property>
  27. <!-- 配置hibernate使用连接池:告知Hibernate使用连接池的厂商 -->
  28. <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
  29. <!-- 配置把Session绑定到当前线程上,从而保证一个线程只有一个Session -->
  30. <property name="hibernate.current_session_context_class">thread</property>
  31. <!-- 3、映射文件的位置 -->
  32. <mapping resource="com/pc/hibernate/domain/Customer.hbm.xml" />
  33. <mapping resource="com/pc/hibernate/domain/LinkMan.hbm.xml" />
  34. </session-factory>
  35. </hibernate-configuration>

发表评论

表情:
评论列表 (有 0 条评论,252人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Hibernate检索策略

    检索策略追求的两个目标是:(以客户和订单的例子说明) 不浪费内存:当Hibernate从数据库中加载Customer对象时,如果同时加载所有关联的Order对象,而程序

    相关 Hibernate检索策略

           有一对有关联关系的对象A和B,在运行Query.list()方法时,先查出A记录,然后遍历A的id去B中查询。这种策略为立即检索策略。这种策略有两处缺点。一处是s

    相关 Hibernate -- 检索

    Hibernate 的检索方式 在实际开发项目时,对数据进行最多的操作就是查询,数据的查询在所有 ORM 框架中都占有极其重要的地位。那么,如何利用 Hibernate