返回介绍

Hibernate 一级缓存

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

Demo

首先看一个非常简单的例子:

@Test
public void test() {
    Customer customer1 = (Customer) session.load(Customer.class, 1);
    System.out.println(customer1.getCustomerName());

    Customer customer2 = (Customer) session.load(Customer.class, 1);
    System.out.println(customer2.getCustomerName());
}

看一下控制台的输出:

Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
Customer1
Customer1

我们可以看到,虽然我们调用了两次 session 的 load 方法,但实际上只发送了一条 SQL 语句。我们第一次调用 load 方法时候,得到了查询结果,然后将结果放到了 session 的一级缓存中。此时,当我们再次调用 load 方法,会首先去看缓存中是否存在该对象,如果存在,则直接从缓存中取出,就不会在发送 SQL 语句了。

但是,我们看一下下面这个例子:

@Test
public void test() {
    Customer customer1 = (Customer) session.load(Customer.class, 1);
    System.out.println(customer1.getCustomerName());
    transaction.commit();
    session.close();
    session = sessionFactory.openSession();
    transaction = session.beginTransaction();
    Customer customer2 = (Customer) session.load(Customer.class, 1);
    System.out.println(customer2.getCustomerName());
}

我们解释一下上面的代码,在第 5、6、7、8 行,我们是先将 session 关闭,然后又重新打开了新的 session,这个时候,我们再看一下控制台的输出结果:

Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
Customer1
Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
Customer1

我们可以看到,发送了两条 SQL 语句。其原因是:Hibernate 一级缓存是 session 级别的,所以如果 session 关闭后,缓存就没了,当我们再次打开 session 的时候,缓存中是没有了之前查询的对象的,所以会再次发送 SQL 语句。

我们稍微对一级缓存的知识点进行总结一下,然后再开始讨论关于二级缓存的内容。

作用

Session 的缓存有三大作用:

  1. 减少访问数据库的频率。应用程序从缓存中读取持久化对象的速度显然比到数据中查询数据的速度快多了,因此 Session 的缓存可以提高数据访问的性能。
  2. 当缓存中的持久化对象之间存在循环关联关系时,Session 会保证不出现访问对象图的死循环,以及由死循环引起的 JVM 堆栈溢出异常。
  3. 保证数据库中的相关记录与缓存中的相应对象保持同步。

小结

  • 一级缓存是事务级别的,每个事务(session) 都有单独的一级缓存。这一级别的缓存是由 Hibernate 进行管理,一般情况下无需进行干预。
  • 每个事务都拥有单独的一级缓存不会出现并发问题,因此无须提供并发访问策略。
  • 当应用程序调用 Session 的 save()、update()、saveOrUpdate()、get() 或 load(),以及调用查询接口的 list()、iterate()(该方法会出现 N+1 问题,先查 id) 方法时,如果在 Session 缓存中还不存在相应的对象,Hibernate 就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate 会根据缓存中对象的状态变化来同步更新数据库。 Session 为应用程序提供了两个管理缓存的方法: evict(Object obj):从缓存中清除参数指定的持久化对象。 clear():清空缓存中所有持久化对象,flush():使缓存与数据库同步。
  • 当查询相应的字段,而不是对象时,不支持缓存。我们可以很容易举一个例子来说明,看一下下面的代码。
@Test
public void test() {
    List<Customer> customers = session.createQuery("select c.customerName from Customer c").list();
    System.out.println(customers.size());
    Customer customer2 = (Customer) session.load(Customer.class, 1);
    System.out.println(customer2.getCustomerName());
}

我们首先是只取出 Customer 的 name 属性,然后又尝试着去 Load 一个 Customer 对象,看一下控制台的输出:

Hibernate:
    select
        customer0_.CUSTOMER_NAME as col_0_0_
    from
        CUSTOMERS customer0_
3
Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
Customer1

这一点其实很好理解,我本身就没有查处 Customer 的所有属性,那我又怎么能给你把所有属性都缓存到这个对象中呢?

我们在讲之前的例子中,提到我们关闭 session 再打开,这个时候一级缓存就不存在了,所以我们再次查询的时候,会再次发送 SQL 语句。那么如果要解决这个问题,我们该怎么做?二级缓存可以帮我们解决这个问题。

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

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

发布评论

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