在JPA2.0以后,缓存分为一级缓存和二级缓存(JPA1.0只支持一级缓存)。
持久化上下文就是JPA的一级缓存,通过在持久化上下文中存储持久化状态实体的快照,既可以进行脏检测,还可以当做持久化实体的缓存。一级缓存属于请求范围级别的缓存。
二级缓存是跨越持久化上下文的,是真正意义上的全局应用缓存,二级缓存通常是用来提高应用程序性能的,它可以避免访问以已经从数据库加载的数据,提高访问未被修改数据对象的速度。
如果二级缓存激活,JPA会先从一级缓存中寻找实体,未找到再从二级缓存中寻找。当二级缓存有效时,就不能依靠事务来保护并发的数据,而是依靠锁策略,如在确认修改后,需要手工处理乐观锁失败等。
注意:二级缓存只能缓存通过EntityManager的find或getReference查询到的实体,以及通过实体的getter方法获取到的关联实体;而通过JPQL查询获得的数据缓存需要配置。
二级缓存通常用来提高性能,同时,使用二级缓存可能会导致提取到“陈旧”数据,也会出现并发写的问题。所以二级缓存最好是用在经常阅读数据,比较少更新数据的情况,而不应该对重要数据使用二级缓存。
对于不同的JPA实现产品,开启二级缓存的方式会有所不同,这里用我们用的是jpa2.1的hibernate5实现,
首先来看一级缓存:只有一条sql语句,说明一级缓存在起作用
User user = entityManager.find(User.class, 5);
User user2 = entityManager.find(User.class, 5);
System.out.println(user);
System.out.println(user2);
二级缓存:和Hibernate一样,一级缓存是默认就开启的,不用我们管理,二级缓存默认是关闭的
User user = entityManager.find(User.class, 5);
transaction.commit();
entityManager.close();
entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
User user2 = entityManager.find(User.class, 5);
两次查询中间,提交了事务,关闭了EntityManger,一级缓存失效,发出两条sql语句,
开启二级缓存配置步骤:
想要减少访问数据库的次数,就要开启二级缓存,像Hibernate一样选择使用ehcache作为二级缓存,
1)配置pom.xml,导入jar包
<!-- hibernate-ehcache -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.2.17.Final</version>
</dependency>
2)把ehcache的配置文件ehcache.xml加进来
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="d:/ehcache/"></diskStore>
<!-- 默认缓存配置 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true" />
</ehcache>
3)在jpa的核心配置persistence.xml文件中,配置开启二级缓存
<!-- name为项目工程名 -->
<persistence-unit name="jpademo">
<!--1、 jap是一个规范,是一个接口,它没有任何的具体实现,这里需要指定一个jpa具体实现 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<!-- 2、链接数据库的基本信息 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpademo" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="123456" />
<!--3、 jpa的具体实现,hibernate的基本属性 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<!-- 配置二级缓存技术提供者 -->
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<!-- 开启查询缓存 -->
<property name="hibernate.cache.use_query_cache" value="true" />
</properties>
注意:
persistence.xml配置文件中 properties 标签前配置缓存策略:
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
shared-cached-mode这个标签有5个值:
a) ALL:缓存所有实体类
b) NONE:所有实体类都不缓存
c) ENABLE-SELECTED: 有@Cacheable(true) 的实体类被缓存(常用)
d) DISABLE-SELECTED: 除了有@cacheable(false)这个注解外的所有实体类
e) UNSPECIFIED:默认值,jpa的实现的默认值
然后二级缓存就开启了,再运行上面实例: 两次查询中间,提交了事务,关闭了EntityManger,只会发起一条sql
jpa使用jpql查询缓存:
persistence.xml配置文件中开启查询缓存:
<property name="hibernate.cache.use_query_cache" value="true" />
query中开启查询缓存:query.setHint(QueryHints.HINT_CACHEABLE, true);
String jpql = "select u from User u";
Query query = entityManager.createQuery(jpql);
query.setHint(QueryHints.HINT_CACHEABLE, true);
List<User> userlist = query.getResultList();
System.out.println(userlist);
参考文章:https://blog.csdn.net/dac55300424/article/details/17145359?utm_source=blogxgwz0