JPA缓存(Hibernate实现)
一级缓存对象
在JPA中我们是通过EntityManager对象操作数据库实现CRUD操作,在它身上装着数据库连接对象以及一级缓存对象。而我们的EntityManager对象又是使用Persistence对象读取JPA配置文件persistence.xml获取到持久化单元(persistence-unit),创建entityManagerFactory对象
entityManagerFactory = Persistence.createEntityManagerFactory("com.xer.jpa");
再通过entityManagerFactory创建而得
EntityManager entityManager = entityManagerFactory.createEntityManager();
当我们具体操作数据库时,例如使用entityManager 对象的find方法(查询一条数据)时,首先会在entityManager对象中去查询是否存在一级缓存对象
如果存在:则直接使用,而不会发送SQL语句
如果不存在:则会发送SQL语句,操作数据库进行查询,数据库会返回一个查询结果,查询出的数据,此时,会将这条数据填充到entityManager的一级缓存中
证明一级缓存存在
@Test
public void testQuery() {
EntityManager entityManager = JPAUtil.getEntityManager();
Student student = entityManager.find(Student.class, 1L);
System.out.println(student);
Student student1 = entityManager.find(Student.class, 1L);
System.out.println(student1);
entityManager.close();
}
会发现我进行了两次查询操作,却只执行了一条SQL,说明第二次并没有向数据库发送SQL而是直接使用了一级缓存中的数据(两次查询的数据条件一致)
我们将在一级缓存中找到对应数据称为一级缓存命中
一级缓存命中
条件:同一个entityManagerFactory,同一个entityManager,同一个OID
- OID:在执行完数据库操作之后,将数据放入到Map中时(缓存对象基本上都是以map形式存在的),会为其生成一个唯一的key:实体类全限定名#id
其中entityKey则被称为OID
二级缓存对象
二级缓存对象默认是未启用的状态,如果想要启用二级缓存需要配置
- 导包
需要导入相应的缓存包
由于我搭建的maven项目,所以在pom中配置即可,只需配置hibernate-ehcache
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.3.8.Final</version>
</dependency>
- 配置persistence.xml
<persistence-unit name="cn.itsource.jpa" transaction-type="RESOURCE_LOCAL">
<!--设置二级缓存扫描策略-->
<!--
ALL:所有的实体类都会被缓存
NONE:所有实体类都不会被缓存
ENABLE_SELECTIVE:标识了@Cacheable(true)注解的实体类将会被缓存
DISABLE_SELECTIVE:将会缓存标识了@Cacheable(false)以外的实体类
UNSPECIFIED:默认值
-->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"></property>
<property name="hibernate.connection.url" value="jdbc:mysql:///mydb"></property>
<property name="hibernate.connection.username" value="xer"></property>
<property name="hibernate.connection.password" value="123456"></property>
<!--
配置二级缓存
二级缓存默认是未启用的,所以需要配置启用
-->
<!--启用二级缓存-->
<property name="hibernate.cache.use_second_level_cache" value="true"></property>
<!--
启用查询缓存
即查询操作时也使用缓存,但是一般是不会开启的
-->
<property name="hibernate.cache.use_query_cache" value="true"></property>
<!--
支持二级缓存的工厂
hibernate的配置文件中关于ehcache的工厂支持配置是错误的
将internal改为ehcache即可
-->
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"></property>
</properties>
</persistence-unit>
- 在需要被缓存的实体类上标注@Cacheable注解
这样我们就算完成了二级缓存开启及配置,接下来测试一下
@Test
public void testQueryT() {
EntityManager entityManager = JPAUtil.getEntityManager();
Teacher t = entityManager.find(Teacher.class, 1L);
System.out.println(t);
Teacher t1 = entityManager.find(Teacher.class, 1L);//一级缓存命中
System.out.println(t1);
entityManager.close();
EntityManager entityManager1 = JPAUtil.getEntityManager();
Teacher t2 = entityManager1.find(Teacher.class, 1L); //二级缓存命中
System.out.println(t2);
Teacher t3 = entityManager1.find(Teacher.class, 1L);//一级缓存命中
System.out.println(t3);
entityManager1.close();
}
可以看到同样的发送了一条SQL语句
总结:
在实际开发中,如果频繁的操作数据库,这样会对数据库造成很大的负担,使用了缓存技术,可以大大的减少访问数据库的次数,从而达到性能最优的效果。但是因为在一个应用中,我们应该保证只有一个entityManagerFactory对象,当把全部实体类都开启二级缓存之后,(每次执行完操作都会自动将数据填充到一级缓存、二级缓存中),一级缓存倒不用担心,因为它每次用完之后会关闭,但是二级缓存则是一直存在的,长时间的缓存数据会将我们的内存撑爆的。那么问题来了,我们应该在什么情况下使用二级缓存呢?
二级缓存使用场景
-
读取大于修改,数据经常被查询,而很少修改
因为我们要保证数据库与缓存数据一致,所以每当用户修改数据之后,就需要将缓存与数据库同步,所以实质上还是会频繁操作数据库,此时使用缓存技术就没多大作用 -
独享控制,数据库数据不能被第三方修改
如果用户使用第三方工具修改了数据库信息,则会导致数据库与缓存中的数据不一致,这时如果没有及时的同步则是会出现很大的问题的。 -
允许出现无效数据
使用缓存技术,难免会出现数据精度丢失(数据库与缓存数据不一致)的情况。故,对数据精度要求高的系统是不能使用缓存技术的 -
数据量不超过内存
缓存技术是将数据缓存在内存中的,如果数据量超过了内存,那后果可想而知