Hibernate JPA 缓存配置

Hibernate JPA 缓存配置

1、一级缓存

一级缓存指的是 EntityManager 级的缓存,对于这样的缓存几乎是一直存在的,也就是说只要用户进行JPA的操作,那么就永远都会存在有一级缓存

新建数据库视图类和初始化数据

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "tb_cache")
public class CacheEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long Id;
    private String name;
    private int age;
}
   @Before
    public void initData(){
        EntityManager entityManager = JpaUtils.getEntityManager();
        entityManager.getTransaction().begin();
        Arrays.asList(
                CacheEntity.builder().name("Sam").age(18).build(),
                CacheEntity.builder().name("Mike").age(58).build(),
                CacheEntity.builder().name("Hom").age(48).build(),
                CacheEntity.builder().name("Nick").age(28).build(),
                CacheEntity.builder().name("Kath").age(28).build()
        ).forEach(cacheEntity -> entityManager.persist(cacheEntity));
        entityManager.getTransaction().commit();
    }

1、使用 fnd() 执行两次查询,并且查询的ID信息都一样:

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFindCache(){
        EntityManager entityManager = JpaUtils.getEntityManager();

        CacheEntity cacheEntity1 = entityManager.find(CacheEntity.class, 1L);
        System.err.println(cacheEntity1);

        System.err.println("--------------华丽的分割线-------------------");

        CacheEntity cacheEntity2 = entityManager.find(CacheEntity.class, 1L);
        System.err.println(cacheEntity2);
    }

查看日志:

Hibernate: 
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=1, name=Sam, age=18)
--------------华丽的分割线-------------------
CacheEntity(Id=1, name=Sam, age=18) // 可以发现第二次查询的时候没有发送SQL语句

PS:可以发现这个时候真正执行的是一次数据库查询,也就是说在同一个EntityManager对象操作之中如果使用 find() 查询,那么同一条数据默认情况下只会査询一次,查询完成之后会将这个数据进行缓存。

2、如果说现在在查询过程之中发生了内容的变更呢?如:第一次查询出来的对象现在要求进行一些修改。

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFindCache2(){
        EntityManager entityManager = JpaUtils.getEntityManager();

        CacheEntity cacheEntity1 = entityManager.find(CacheEntity.class, 1L);
        cacheEntity1.setName("SamXX"); // 第一次查询的数据发生改变
        System.err.println(cacheEntity1);

        System.err.println("--------------华丽的分割线-------------------");

        CacheEntity cacheEntity2 = entityManager.find(CacheEntity.class, 1L);
        System.err.println(cacheEntity2);
    }

查看日志:

Hibernate: 
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=1, name=SamXX, age=18)
--------------华丽的分割线-------------------
CacheEntity(Id=1, name=SamXX, age=18) // 可以发现第二次查询的时候可以发送SQL语句

PS:第一个数据读取进来之后由于发生了改变,肯定和原始数据库的内容不一样了,那么此时通过第二次查询获得的结果可以发现,依然只是查询了一次,并且在ID不改变的情况下,会发生相同ID的查询依然引用已有的缓存对象.

3、如果现在想内容进行更新,需要采用刷新的模式完成。entityManager.refresh(object)

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFindRefresh(){
        EntityManager entityManager = JpaUtils.getEntityManager();

        CacheEntity cacheEntity1 = entityManager.find(CacheEntity.class, 1L);
        cacheEntity1.setName("SamXX"); // 第一次查询的数据发生改变
        System.err.println(cacheEntity1);
        entityManager.refresh(cacheEntity1); // 重新加载,会再次发送SQL

        System.err.println("--------------华丽的分割线-------------------");

        CacheEntity cacheEntity2 = entityManager.find(CacheEntity.class, 1L);
        System.err.println(cacheEntity2);
    }

查看日志:

Hibernate: 
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=1, name=SamXX, age=18)
Hibernate: 
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
--------------华丽的分割线-------------------
CacheEntity(Id=1, name=Sam, age=18)

此时同样操作发现重复查询了两次,主要是因为使用了refresh()明确表示该缓存对象需要重新加载。在JPA中有一个非常重要的概念:JPA 的对象状态,在JPA 里面一共有四种对象状态.

2、对象状态(生命周期)

参考文献 & 鸣谢:JPA实体的四种状态:https://www.jianshu.com/p/636954880af8

JPA 对象四种状态(老版中只有三种,没有移除态):

  1. 瞬时态对象(瞬时态:New or Transient):尚未有id,还未和 Persistence Context 建立关联的对象
  2. 持久态对象(持久态:Managed or Persistent):有id值,与 Persistence Context 建立了关联的对象
  3. 游离态对象(游离态:Datached):有id值,没有和 Persistence Context 建立关联的对象
  4. 删除的对象(移除态:Removed):有id值,尚未和 Persistence Context 有关联,但是已经准备好从数据库中删除(确切的说在事物提交前还与 Persistence Context 有关联,事物提交后就与 Persistence Context 没有关联了)

  1. 临时状态/瞬时状态(transient):没有与 entityManager 发生关系,没有被持久化,不处于 entityManager 中的对象
  2. 持久化状态/托管状态(persistent):与 entityManager 发生关系,已经被持久化,加入到 entityManager 的一级缓存中的对象
  3. 删除状态(removed):调用了 entityManager.remove(obj),对象有关联的ID,并且在 entityManager 管理下。调用remove方法后已经计划删除,事物提交后才会被真正从数据库删除
  4. 游离状态(detached):脱管状态:对象和 entityManager 解除关系
                                            持久状态     
                                            |     ⬆     
                                         find() flush() 
                                            ⬇     |     
New POJO() ----> 瞬时状态 ----persist()----> 托管状态 ----remove()----> 销毁状态
                                            |     ⬆ 
                                        事物提交 meger/refresh
                                            ⬇     |
                                            游离状态
img

1、示例说明:

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFindAdd(){
        CacheEntity cacheEntity = new CacheEntity(); // 临时状态
        cacheEntity.setName("lsx");
        cacheEntity.setAge(16);
        EntityManager entityManager = JpaUtils.getEntityManager();
        entityManager.getTransaction().begin();
        entityManager.persist(cacheEntity); // 持久状态
        entityManager.remove(cacheEntity); // 删除状态
        entityManager.getTransaction().commit();// 提交更新事务
        entityManager.close();
        System.err.println(cacheEntity);// 游离状态
    }

2、观察在持久态下的数据更新(重点)

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFindEdit(){
        EntityManager entityManager = JpaUtils.getEntityManager();
        // 持久态
        CacheEntity cacheEntity = entityManager.find(CacheEntity.class, 1L);
        entityManager.getTransaction().begin(); // 开启事务
        cacheEntity.setName("SamXX"); // 持久化数据发生改变
        entityManager.getTransaction().commit(); // 提交更新事务(发现我们并没有提交更新操作)
        entityManager.close();
    }

查看日志:

Hibernate: // entityManager.find(CacheEntity.class, 1L);
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
Hibernate: // 更新了持久态中的属性内容,并且提交了事务
    update
        tb_cache 
    set
        age=?,
        name=? 
    where
        Id=?
-- 然后查看数据库这条数据:
mysql> select * from tb_cache where id = 1;
+----+-----+-------+
| Id | age | name  |
+----+-----+-------+
|  1 |  18 | SamXX |
+----+-----+-------+
1 row in set (0.02 sec)

PS:发现这条数据name发生了改变,但是我们并没有执行更新方法,可以得出结论:持久态话下的数据发生改变的话,只要在事务中并且提交了事务更新,那么默认就相当于执行了更新数据库操作

3、一个新的对象默认情况下属于瞬时态,瞬时态也可以持久化

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFindAdd2(){
        CacheEntity cacheEntity = new CacheEntity(); // 临时状态
        cacheEntity.setName("lsx");
        cacheEntity.setAge(16);

        EntityManager entityManager = JpaUtils.getEntityManager();
        entityManager.getTransaction().begin();// 开启事务
        entityManager.persist(cacheEntity);// 数据持久化,自动可以获取ID
        entityManager.getTransaction().commit();// 提交更新事务
        System.err.println("-----新增数据的ID是:" + cacheEntity.getId());

        // 将刚刚保存对象信息根据ID查询出来
        CacheEntity selectCacheEntity = entityManager.find(CacheEntity.class, cacheEntity.getId());
        System.err.println(selectCacheEntity);
        entityManager.close();
    }

查看日志:

Hibernate: 
    insert 
    into
        tb_cache
        (age, name) 
    values
        (?, ?)
-----新增数据的ID是:6
CacheEntity(Id=6, name=lsx, age=16) // 可以发现查询数据没有发送SQL,而是从缓存中获取

PS:由于现在使用了persisit()方法将瞬时态对象变为了持久态,所以这个对象就会被缓存起来,那么再执行一次查询的时候就不会重复发出查询命令,而是直接使用缓存中的数据.

4、如果每新增一个数据都被缓存起来,当批量新增时,缓存所占用的空间就会出现严重的不足。最好的做法是进行:强制性的保存以及清空

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFindFlushAndClear(){
        CacheEntity cacheEntity = new CacheEntity();
        cacheEntity.setName("lsx");
        cacheEntity.setAge(16);

        EntityManager entityManager = JpaUtils.getEntityManager();
        entityManager.getTransaction().begin();

        entityManager.persist(cacheEntity);// 数据持久化,自动可以获取ID
        entityManager.flush(); // 强制立即写入数据库
        entityManager.clear(); // 清空缓存

        entityManager.getTransaction().commit();// 提交更新事务
        System.err.println("-----新增数据的ID是:" + cacheEntity.getId());

        // 将刚刚保存对象信息根据ID查询出来
        CacheEntity selectCacheEntity = entityManager.find(CacheEntity.class, cacheEntity.getId());
        System.err.println(selectCacheEntity);
        entityManager.close();
    }

查看日志:

Hibernate: 
    insert 
    into
        tb_cache
        (age, name) 
    values
        (?, ?)
-----新增数据的ID是:6
Hibernate: 
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=6, name=lsx, age=16)

PS:由于清除了缓存,所以此时的数据再次查询的时候就需要重新发出查询指令,当然重新查询之后也就意味这一个对象重新被缓存了

所以在真实进行数据的批量增加时,我们应该适当加上强制写入和清空缓存(例如每新增10条数据后就执行一次flushclear方法)

5、删除数据(游离状态)

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testDelete(){
        EntityManager entityManager = JpaUtils.getEntityManager();
        CacheEntity cacheEntity = entityManager.find(CacheEntity.class, 1L);
        entityManager.getTransaction().begin();// 开启事务
        entityManager.remove(cacheEntity); // 删除数据
        cacheEntity.setName("小明");
        entityManager.getTransaction().commit();// 提交事务
        entityManager.close();
    }

查看日志:

Hibernate: 
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
Hibernate: 
    delete 
    from
        tb_cache 
    where
        Id=?

PS:删除的数据就属于游离态了,所以此时已无法实现试据的持久态管理了

3、二级缓存

一级缓存时针对与EntityManager的缓存处理,并且永久存在,而二级缓存指的是针对于多个EntityManager实现的缓存处理,但是二级缓存默认是没有开启的

1、建立两个不同的 EntityManager 进行数据查询

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFind(){
        EntityManager entityManagerA = JpaUtils.getEntityManager();
        System.err.println(entityManagerA.find(CacheEntity.class, 1L));
        entityManagerA.close();

        System.err.println("--------------华丽的分割线-------------------");

        EntityManager entityManagerB = JpaUtils.getEntityManager();
        System.err.println(entityManagerB.find(CacheEntity.class, 1L));
        entityManagerB.close();
    }

查看日志:

Hibernate: 
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=1, name=Sam, age=18)
--------------华丽的分割线-------------------
Hibernate: 
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=1, name=Sam, age=18)

可以发现不同的Seeson(EntityManager)查询同一条数据的时候依然发出了两次查询指令,所以此时表示JPA中没有开启二级缓存

2、二级缓存一般用第三方组件:Redis、Ehache,这里用Ehcache为例,pox.xml引入Ehcache依赖

        <!--添加Hibernate-Ehcache包,版本号与hibernate一样 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>5.4.1.Final</version>
        </dependency>
        <!--Ehcache-core 包 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.6.11</version>
        </dependency>

3、在resources目录下新建一个**ehcache.xml**文件。如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
        updateCheck="false">
   <!--
      diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
      - user.home – 用户主目录
      - user.dir – 用户当前工作目录
      - java.io.tmpdir – 默认临时文件路径
      - 或自定义一个本地磁盘路径例如:/home、./tmpdir/Tmp_EhCache
    -->
   <!-- 磁盘缓存位置 --> 
   <diskStore path="./tmpdir/Tmp_EhCache"/>
   
   <defaultCache
           eternal="false"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="259200"
           memoryStoreEvictionPolicy="LRU"/>

   <cache
           name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>
   <!--
      defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
    -->
   <!--
     name:缓存名称。
     maxElementsInMemory:缓存最大数目
     maxElementsOnDisk:硬盘最大缓存个数。
     eternal:对象是否永久有效,一但设置了,timeout将不起作用。
     overflowToDisk:是否保存到磁盘,当系统当机时
     timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
     timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
     diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
     diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
     diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
     memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
     clearOnFlush:内存数量最大时是否清除。
     memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
     FIFO,first in first out,这个是大家最熟的,先进先出。
     LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
     LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
  -->
</ehcache>

4、在JPA配置文件persistence.xml中增加二级缓存配置

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">

    <!-- 持久化单元:持久化单元事务类型,RESOURCE_LOCAL:本地事务管理 -->
    <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <!--
            配置二级缓存时候使用的模式,可配置值有:
            - ALL:所有的实体类都被缓存
            - NONE:所有的实体类都不被缓存
            - ENABLE_SELECTIVE:标识@Cacheable(true)注解的实体类将被缓存
            - DISABLE_SELECTIVE;缓存除标识@Cacheable(false)以外的所有实体类
            - UNSPECIFIED:默认值,JPA 产品默认值将被使用
         -->
        <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>

        <!--可选配置:配置jpa实现方的配置信息-->
        <properties>
            <!-- 数据库信息配置:数据库驱动、数据库地址、数据库账户、数据库密码 -->
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.url" value="jdbc:mysql://127.0.0.1:3306/hibernate_jpa"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="password"/>
            
            <!-- 配置JPA服务提供商可选参数 -->
            <property name="hibernate.show_sql" value="true" /><!-- 自动显示sql -->
            <property name="hibernate.format_sql" value="true"/><!-- 格式化sql -->
            <property name="hibernate.hbm2ddl.auto" value="update" /><!-- 自动建表:none,create,update,create-drop,validate -->

            <!-- 二级缓存相关 -->
            <!-- 开启二级缓存 -->
            <property name="hibernate.cache.use_second_level_cache" value="true"/>
            <!-- 配置二级缓存处理类 -->
            <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
            <!-- 开启查询缓存,entityManager.find查询可以不配置,如果使用JPQL或SQL查询需要开启该配置 -->
            <property name="hibernate.cache.use_query_cache" value="true"/>
            <!-- 指定缓存配置文件位置,如果默认在resources下可不配置 -->
            <property name="hibernate.cache.provider_configuration" value="classpath:ehcache.xml"/>

        </properties>
    </persistence-unit>
</persistence>

5、其中ENABLE_SELECTIVE模式为实体类上配置 @Cacheable(true) 的才会进行生效

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "tb_cache")
@Cacheable(value = true) // 增加此注解即可,value默认为true,直接配置@Cacheable也可以
public class CacheEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long Id;
    private String name;
    private int age;
}
/***************另一种用法(没有特殊需求只用上面配置即可)******************/
/**
 * 如果只配置@Cacheable的话是用ehcache.xml中默认defaultCache配置,
 * 想用自定义名称的缓存配置,需要增加@Cache注解,例如想使用ehcache.xml中的<cache name="cloud_user"../> 缓存配置
 * 增加注解 @Cache(usage = CacheConcurrencyStrategy.READ_WRITE,region="指定的cache name")
 **/
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE,region="cloud_user")
@Cacheable(value = true)
@Entity
@Table(name = "tb_cache")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CacheEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long Id;
    private String name;
    private int age;
}

usage提供缓存对象的事务隔离机制有这几种:NONE、READ_ONLY、 NONSTRICT_READ_WRITE、READ_WRITE、TRANSACTIONAL

  1. NONE:默认什么都不做
  2. READ_ONLY:只读模式,在此模式下,如果对数据进行更新操作,会有异常
  3. READ_WRITE:读写模式在更新缓存的时候会把缓存里面的数据换成一个锁,其它事务如果去取相应的缓存数据,发现被锁了,直接就去数据库查询
  4. NONSTRICT_READ_WRITE:不严格的读写模式则不会的缓存数据加锁
  5. TRANSACTIONAL:事务模式指缓存支持事务,当事务回滚时,缓存也能回滚,只支持JTA环境。

Ehcache不支持transaction事务机制,但其他三种可以使用:

  1. read-only:无需修改, 那么就可以对其进行只读 缓存,注意,在此策略下,如果直接修改数据库,即使能够看到前台显示效果,但是将对象修改至cache中会报error,cache不会发生作用。另:删除记录会报错,因为不能在read-only模式的对象从cache中删除
  2. read-write:需要更新数据,那么使用读/写缓存 比较合适,前提:数据库不可以为serializable transaction isolation level(序列化事务隔离级别)
  3. nonstrice-read-write:只偶尔需要更新数据(也就是说,两个事务同时更新同一记录的情况很不常见),也不需要十分严格的事务隔离,那么比较适合使用非严格读/写缓存策略。

6、再次测试Test代码:

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFind(){
        EntityManager entityManagerA = JpaUtils.getEntityManager();
        System.err.println(entityManagerA.find(CacheEntity.class, 1L));
        entityManagerA.close();

        System.err.println("--------------华丽的分割线-------------------");

        EntityManager entityManagerB = JpaUtils.getEntityManager();
        System.err.println(entityManagerB.find(CacheEntity.class, 1L));
        entityManagerB.close();
    }

查看日志:

Hibernate: 
    select
        cacheentit0_.Id as Id1_2_0_,
        cacheentit0_.age as age2_2_0_,
        cacheentit0_.name as name3_2_0_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=1, name=Sam, age=18)
--------------华丽的分割线-------------------
CacheEntity(Id=1, name=Sam, age=18) // 可以发现第二次查询没有发送查询语句了

3、查询缓存

在进行数据查询都是直接EntityManager的方法直接查询,这种方式可以帮助用户直接进行缓存处理,如果现在使用Query查询,则对于缓存配置就不生效了

PS:而使用Query查询是没有缓存的,需要设置:query.setHint(QueryHints.HINT_CACHEABLE, true);(且每新创建一个Query都要设置)

由于在二级缓存中已经配置了如下配置,我们先注释掉下面的配置或者关闭配置开始测试:

<!-- 开启查询缓存,entityManager.find查询可以不配置,如果使用JPQL或SQL查询需要开启该配置,并设置query.setHint() -->
<!--<property name="hibernate.cache.use_query_cache" value="true"/>-->
<property name="hibernate.cache.use_query_cache" value="false"/>

1、观察默认情况下Query查询处理(分别测试开启查询缓存和关闭查询缓存)

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFindQuery(){
        String jpql = "from CacheEntity where Id = :Id";
        EntityManager entityManagerA = JpaUtils.getEntityManager();
        TypedQuery<CacheEntity> queryA = entityManagerA.createQuery(jpql, CacheEntity.class);
        queryA.setParameter("Id", 1L);
        System.err.println(queryA.getSingleResult());
        entityManagerA.close();

        System.err.println("--------------华丽的分割线-------------------");

        EntityManager entityManagerB = JpaUtils.getEntityManager();
        TypedQuery<CacheEntity> queryB = entityManagerB.createQuery(jpql, CacheEntity.class);
        queryB.setParameter("Id", 1L);
        System.err.println(queryB.getSingleResult());
        entityManagerB.close();
    }

查看日志:

Hibernate: 
    select
        cacheentit0_.Id as Id1_2_,
        cacheentit0_.age as age2_2_,
        cacheentit0_.name as name3_2_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=1, name=Sam, age=18)
--------------华丽的分割线-------------------
Hibernate: 
    select
        cacheentit0_.Id as Id1_2_,
        cacheentit0_.age as age2_2_,
        cacheentit0_.name as name3_2_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=1, name=Sam, age=18)

PS:发现默认情况下即便开启或者固安必二级查询缓存,对于Query查询也是无效的,所以还是需要开启查询缓存配置(后面需要加一个配置才能生效)

2、开启查询缓存配置(还原注释掉的配置),使用QueryHints操作

    // 运行之前,修改hibernate.hbm2ddl.auto=create
    @Test
    public void testFindQuery2(){
        String jpql = "from CacheEntity where Id = :Id";
        EntityManager entityManagerA = JpaUtils.getEntityManager();
        TypedQuery<CacheEntity> queryA = entityManagerA.createQuery(jpql, CacheEntity.class);
        queryA.setHint(QueryHints.HINT_CACHEABLE,true);
        queryA.setParameter("Id", 2L);
        System.err.println(queryA.getSingleResult());
        entityManagerA.close();

        System.err.println("--------------华丽的分割线-------------------");

        EntityManager entityManagerB = JpaUtils.getEntityManager();
        TypedQuery<CacheEntity> queryB = entityManagerB.createQuery(jpql, CacheEntity.class);
        queryB.setHint(QueryHints.HINT_CACHEABLE,true);
        queryB.setParameter("Id", 2L);
        System.err.println(queryB.getSingleResult());
        entityManagerB.close();
    }

查看日志:

Hibernate: 
    select
        cacheentit0_.Id as Id1_2_,
        cacheentit0_.age as age2_2_,
        cacheentit0_.name as name3_2_ 
    from
        tb_cache cacheentit0_ 
    where
        cacheentit0_.Id=?
CacheEntity(Id=2, name=Mike, age=58)
--------------华丽的分割线-------------------
CacheEntity(Id=2, name=Mike, age=58) // 可以发现只发送了一次查询操作

PS:使用Query查询是没有缓存的,需要设置:query.setHint(QueryHints.HINT_CACHEABLE, true);(且每新创建一个Query都要设置)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值