Hibernate 一级缓存和快照


一、Hibernate的一级缓存

Hibernate的一级缓存就是指Session缓存。通过查看Session接口的实现类——SessionImpl.java的源码可发现有如下两个类:

  • actionQueue它是一个行动队列,它主要记录crud操作的相关信息。
  • persistenceContext它是持久化上下文,它其实才是真正的缓存。

Session中定义了一系列的集合来存储数据,它们构成了Session的缓存。只要Session没有关闭,它就会一直存在。当我们通过Hibernate中的Session提供的一些API例如save()、get()、update()等进行操作时,就会将持久化对象保存到Session中,当下一次再去查询缓存中具有的对象(通过OID值来判断),就不会去从数据库中查询了,而是直接从缓存中获取。Hibernate的一级缓存存在的目的就是为了减少对数据库的访问。 
当然了,在Hibernate中还有一个二级缓存,它是SessionFactory级别缓存,后面文章再做介绍。

1.1、演示一级缓存的存在

现在我举例来演示一级缓存的存在。首先我们肯定要搭建好Hibernate的开发环境,读过前面文章的童鞋,应该可以快速搭建好的,在此不做过多赘述。 
在com.yiidian.test包下新建一个单元测试类——Demo.java,我们首先编写如下方法来测试一级缓存的存在:

  // 测试一级缓存
    @Test
    public void test1() {
        // 1.得到session
        Session session = HibernateUtil.getSession();
        session.beginTransaction();

        Customer customer = session.get(Customer.class, 1); // 查询id=1的Customer对象,如果查询到,会将Customer对象存储到一级缓存中
        Customer customer2 = session.get(Customer.class, 1); // 会从一级缓存中查询,而不会向数据库再发送sql语句查询

        // 2.事务提交,并关闭session
        session.getTransaction().commit();
        session.close();
    }

首次查询id为1的Customer对象时,Hibernate会向MySQL数据库发送如下SQL语句:

Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.name as name2_0_0_,
        customer0_.gender as gender3_0_0_ 
    from
        t_customer customer0_ 
    where
        customer0_.id=?

而第二次查询时,并没有向MySQL数据库发送select语句。这是因为首次查询id为1的Customer对象时,如果查询到,就会将Customer对象存储到一级缓存中,第二次查询时,会从一级缓存中查询,而不会向数据库再发送select语句查询。

二、持久化对象具有自动更新数据库的能力

现在我举例来演示持久化对象具有自动更新数据库的能力。在Demo单元测试类中编写如下方法:

// 持久化对象具有自动更新数据库的能力
    @Test
    public void test2() {
        // 1.得到session
        Session session = HibernateUtil.getSession();
        session.beginTransaction();
        Customer customer = session.get(Customer.class, 1); // 查询id=1的Customer对象,如果查询到,会将Customer对象存到一级缓存中
        customer.setName("Tom"); // 操作持久化对象来修改属性
        // 2.事务提交,并关闭session
        session.getTransaction().commit();
        session.close();
    }

测试以上test2方法,将发现数据库t_customer表中,id为1的那条记录的name字段变为Tom,这足以说明持久化对象具有自动更新数据库的能力了。但是为什么持久化对象具有自动更新数据库的能力呢?原因涉及到一个概念——快照,快照就是当前一级缓存里面对象的散装数据(对象的属性,如name、id……)。当执行完以下这句代码:

Customer customer = session.get(Customer.class, 1);

就会向一级缓存中存储数据,一级缓存其底层使用了一个Map集合来存储,Map的key存储的是一级缓存对象,而value存储的是快照。通过在这句代码上打个断点,然后以debug的方式运行,Watch一下session会看得更加清楚,如下: 

接着执行以下这句代码:

customer.setName("Tom");

执行完毕会修改一级缓存中的数据,如下: 

三、一级缓存常用API

一级缓存特点:

  • 当我们通过session的save、update、saveOrUpdate方法进行操作时,如果一级缓存中没有对象,那么会从数据库中查询到这些对象,并存储到一级缓存中。
  • 当我们通过session的load、get、Query的list等方法进行操作时,会先判断一级缓存中是否存在数据,如果没有才会从数据库获取,并且将查询的数据存储到一级缓存中。
  • 当调用session的close方法时,session缓存将清空。

一级缓存常用的API:

  • clear():清空一级缓存。
  • evict():清空一级缓存中指定的某个对象。
  • refresh():重新查询数据库,用数据库中的信息来更新一级缓存与快照区。

 现在我举例来演示一级缓存常用的API。在Demo单元测试类中编写如下方法:

// 测试一级缓存操作常用的API
    @Test
    public void test3() {
        // 1.得到session
        Session session = HibernateUtil.getSession();
        session.beginTransaction();

        // 操作
        List<Customer> list = session.createQuery("from Customer").list(); // 这个操作会存储数据到一级缓存
        session.clear(); // 清空一级缓存
        Customer c = session.get(Customer.class, 1); // 会先从session的一级缓存中获取,如果不存在,才会从数据库里面获取

        session.evict(c); // 从一级缓存中删除一个指定的对象
        Customer cc = session.get(Customer.class, 1);

        cc.setName("kkkk");
        session.refresh(cc); // refresh方法的作用是:它会用数据库里面的数据来同步我们的一级缓存以及快照区,
                             // 这样的话,再操作cc时,就不会发送update语句。
                             // refresh方法:重新查询数据库,用数据库中的信息来更新一级缓存与快照区

        // 2.事务提交,并关闭session
        session.getTransaction().commit();
        session.close();        
    }

可通过debug方式运行,这样会看得更清楚。在此不做过多赘述,读者自行操作。

 

源码下载:https://pan.baidu.com/s/1i4BN4Kd