返回介绍

1-N 和 N-N 的检索策略

发布于 2025-01-04 01:27:29 字数 6729 浏览 0 评论 0 收藏 0

知识点

我在之前的博客中对 1-N 和 N-N 有过学习,所以我假设读者已经了解了 Hibernate 中关于 1-N 和 N-N 的映射。我们建立了 Customer 与 Order 的 1-N 关联关系,表示一个顾客可以有多个订单。

我们在映射文件中,用元素来配置 1-N 关联以及 N-N 关联关系。元素有 lazy、fetch 和 batch-size 属性。

  • lazy: 主要决定 orders 集合被初始化的时机。即到底是在加载 Customer 对象时就被初始化,还是在程序访问 orders 集合时被初始化。
  • fetch: 取值为 “select” 或 “subselect” 时,决定初始化 orders 的查询语句的形式;若取值为”join”, 则决定 orders 集合被初始化的时机
  • 若把 fetch 设置为 “join”, lazy 属性将被忽略
  • 元素的 batch-size 属性:用来为延迟检索策略或立即检索策略设定批量检索的数量. 批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能。
lazy 属性
(默认值 true)
fetch 属性
(默认值 select)
检索策略
true未设置
(取默认值 select)
采用延迟检索策略,这是默认的检索策略,也是优先考虑使用的检索策略
false未设置
(取默认值 select)
采用立即索策略,当使用 Hibernate 二级缓存时可以考虑使用立即检索
extra未设置
(取默认值 select)
采用加强延迟检索策略,它尽可能的延迟 orders 集合被初始化的时机
true,extra
or extra
未设置
(取默认值 select)
lazy 属性决定采用的检索策略,即决定初始化 orders 集合的时机。fetch 属性为 select,意味
着通过 select 语句来初始化 orders 的集合,形式为 SELECT * FROM orders WHERE customer
_id IN (1,2,3,4)
true,extra
or extra
subselectlazy 属性决定采用的检索策略,即决定初始化 orders 集合的时机。fetch 属性为 subselect,意味
着通过 subselect 语句来初始化 orders 的集合,形式为 SELECT * FROM orders WHERE
customer_id IN (SELECT id FROM customers)
truejoin采采用迫切左外连接策略

Lazy

我们现在开始研究一下关于元素的 lazy 属性。

Demo

首先我们看一下延迟检索,也就是属性的 lazy 为 true 或者不设置的情况下:

@Test
public void testOne2ManyLevelStrategy() {
        Customer customer = (Customer) session.get(Customer.class, 1);
        System.out.println(customer.getCustomerName());
    System.out.println(customer.getOrders().getClass());
        System.out.println(customer.getOrders().size());
}

下面是控制的输出结果

Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
AA
class org.hibernate.collection.internal.PersistentSet
Hibernate:
    select
        orders0_.CUSTOMER_ID as CUSTOMER3_0_1_,
        orders0_.ORDER_ID as ORDER_ID1_1_1_,
        orders0_.ORDER_ID as ORDER_ID1_1_0_,
        orders0_.ORDER_NAME as ORDER_NA2_1_0_,
        orders0_.CUSTOMER_ID as CUSTOMER3_1_0_
    from
        ORDERS orders0_
    where
        orders0_.CUSTOMER_ID=?
3

从结果中可以明显的看出,Hibernate 使用了延迟检索。其中的 orders 并没有初始化,而是返回了一个集合代理对象。当我们通过 customer.getOrders().size() 这段代码真正要使用 orders 集合的时候,才发送 SQL 语句进行查询。

在延迟检索(lazy 属性值为 true)集合属性时,Hibernate 在以下情况下初始化集合代理类实例:

  • 应用程序第一次访问集合属性: iterator(), size(), isEmpty(), contains() 等方法
  • 通过 Hibernate.initialize() 静态方法显式初始化

下面我们将的 lazy 属性修改为 false,如 <set name="orders" table="ORDERS" inverse="true" lazy="false">

修改完之后再执行测试代码,输出结果也就很明显了,在调用 load() 方法时,会先执行 SQL 语句取出 Customer 以及相关联的 orders。

最后,提一下 lazy 的另一个取值 extra。 该取值与 true 类似,主要区别是增强延迟检索策略能够进一步延迟 Customer 对象的 orders 集合代理实例的初始化时机。

首先我们将元素中的 lazy 设为 extra。我们同样的执行上文中的单元测试代码, 得到以下结果:

Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
AA
class org.hibernate.collection.internal.PersistentSet
Hibernate:
    select
        count(ORDER_ID)
    from
        ORDERS
    where
        CUSTOMER_ID =?
3

我们观察第二个 SQL 语句。我们发现他并没有对 orders 进行初始化,而是通过使用一个 count() 函数。extra 取值为增强的延迟检索,该取值会尽可能的延迟集合初始化的时机。

例如:当我们将 lazy 设置为 true(延迟检索),而我们调用 order.size() 方法的时候,这个时候就会通过 SQL 将 orders 集合初始化。但现在我们用 extra 这个属性,发现我们调用 orders 的 size 方法,并没有初始化,而是通过了一个 count 函数。

所以我们得到结论:

增强延迟检索策略能进一步延迟 Customer 对象的 orders 集合代理实例的初始化时机:

  • 当程序第一次访问 order 属性的 size(), contains() 和 isEmpty() 方法时, Hibernate 不会初始化 orders 集合类的实例,仅通过特定的 select 语句查询必要的信息,不会检索所有的 Order 对象。
  • 当程序第一次访问 orders 属性的 iterator() 方法时,会导致 orders 集合代理类实例的初始化。(这个是没办法的= =)

但其实我们在实际的开发过程中,当我们要用到 size 或者 contains 等方法的时候,基本上代表我们就要用到集合部分的属性。如果我们选用 extra 的话,反倒会多发送 SQL 语句。

关于 extra 的其他点大家可以自己进行一些测试,比较简单方便。

BatchSize

元素有一个 batch-size 属性,用来为延迟检索策略或立即检索策略设定批量检索的数量。批量检索能减少 SELECT 语句的数目,提高延迟检索或立即检索的运行性能。 ### Demo 首先说一下这个 Demo 中用到的数据的数据,Customers 表中共有 4 条数据。而我们的 lazy 属性设为 true,即采用延迟加载策略。 我们看一下下面的代码: ```java @Test public void testSetBatchSize() { List customers = session.createQuery("FROM Customer").list(); System.out.println(customers.size()); for (Customer customer : customers) { if (customer.getOrders() != null) System.out.println(customer.getOrders().size()); } } ``` 首先,在该代码的第三行我们获取了所有的 Customer,得到 list 集合(集合中存的是 Customer 对象),然后遍历该 list 集合;每次循环,我们都通过 customer.getOrders 方法来实例化 orders 集合。 篇幅原因,我在这不贴控制台的输出了。但可以想到的是,会一共发出 5 条 SQL。第一条是获取 customer 集合,因为集合有 4 个 customer 对象,所以会再发出 4 条 SQL 来分别初始化。 好了,现在我们修改元素,在里边加入`batch-size="4"`,所以现在的元素的代码为``。 我们重新运行单元测试代码,得到结果: ``` Hibernate: select customer0_.CUSTOMER_ID as CUSTOMER1_0_, customer0_.CUSTOMER_NAME as CUSTOMER2_0_ from CUSTOMERS customer0_ 4 Hibernate: select orders0_.CUSTOMER_ID as CUSTOMER3_0_1_, orders0_.ORDER_ID as ORDER_ID1_1_1_, orders0_.ORDER_ID as ORDER_ID1_1_0_, orders0_.ORDER_NAME as ORDER_NA2_1_0_, orders0_.CUSTOMER_ID as CUSTOMER3_1_0_ from ORDERS orders0_ where orders0_.CUSTOMER_ID in ( ?, ?, ?, ? ) 3 3 3 0 ``` 我们看到这个时候只有 2 条 SQL 语句。第一条同样的还是得到 customer,而第二条 SQL 语句直接全部初始化了 4 个 orders 集合。这就是 batch-size 的作用所在。我们可以想到的是,如果我们将 batch-size 设为 2,则是 3 条 SQL 语句。**也就是我们上文中提到的批量检索。** ## Fetch 元素的 fetch 属性是用于确定初始化 orders 集合的方式。 1. 默认值为 orders,也就是通过正常的方式来初始化 set 元素。例如我们在将 batch-size 例子的时候,我们并没有设置 fetch 属性(默认即为 select),所以我们在初始化 orders 集合的时候,会发现在 SQL 语句中是通过`where orders0_.CUSTOMER_ID in (?, ?, ?, ?)`这种方式来进行初始化的。 2. 可以取值为 subselect,我们看名字也能知道大概意思,就是通过子查询的方式来初始化所有的 set 集合。例如我们设置为 subselect 后,可看到 SQL 语句中包含`where orders0_.CUSTOMER_ID in (select customer0_.CUSTOMER_ID from CUSTOMERS customer0_)`。**子查询作为 WHERE 子句的 in 的条件出现的,子查询查询 1 的一端的 ID,此时 lazy 属性是有效的,但 batch-size 属性失效。** 3. 若取值为 join: + 在加载 1 的一端的对象的时候,使用迫切左外连接(可参考该博客进行学习[Hibernate:深入 HQL 学习](http://tracylihui.github.io/2015/07/08/Hibernate%EF%BC%9A%E6%B7%B1%E5%85%A5HQL%E5%AD%A6%E4%B9%A0/))的方式检索 N 的一端的集合的属性; + 忽略 lazy 属性(理解了迫切左外连接,也就能知道为啥会忽略 lazy 属性了); + HQL 查询忽略`fetch="join"`的取值; + **Query 的 list() 方法会忽略映射文件中配置的迫切左外连接检索策略,而依旧采用延迟加载策略。(这个点之前测试的时候老是忽略掉了)** # 总结 以上就是关于 Hibernate 检索策略的学习,但并不全。将在下一篇中,对 N-1 和 1-1 的检索策略进行学习,并做一个总结。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文