介绍

正如我前面所解释的 ,企业的高速缓存需要勤奋。 由于数据在数据库( 记录系统 )和缓存层之间重复,因此我们需要确保两个单独的数据源不会分开。

如果缓存的数据是不可变的(数据库和缓存都无法修改它),我们可以安全地对其进行缓存,而不必担心任何一致性问题。 只读数据始终是应用程序级缓存的理想选择,可以在不放松一致性保证的情况下提高读取性能。

只读二级缓存

为了测试只读二级缓存策略,我们将使用以下域模型:

存储库是根实体,是任何Commit实体的父代。 每个提交都有一个“ 更改”组件(可嵌入的值类型)列表。

所有实体都缓存为只读元素:

@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY
)

持久实体

只读二级缓存使用一种直读缓存策略,在获取时会缓存实体。

doInTransaction(session -> {Repository repository = new Repository("Hibernate-Master-Class");session.persist(repository);
});

当一个实体持久化时,只有数据库包含该实体的副本。 首次获取实体时,记录系统将传递到缓存层。

@Test
public void testRepositoryEntityLoad() {LOGGER.info("Read-only entities are read-through");doInTransaction(session -> {Repository repository = (Repository) session.get(Repository.class, 1L);assertNotNull(repository);});doInTransaction(session -> {LOGGER.info("Load Repository from cache");session.get(Repository.class, 1L);});
}

此测试生成输出:

--Read-only entities are read-throughSELECT readonlyca0_.id   AS id1_2_0_,readonlyca0_.NAME AS name2_2_0_
FROM   repository readonlyca0_
WHERE  readonlyca0_.id = 1 --JdbcTransaction - committed JDBC Connection--Load Repository from cache--JdbcTransaction - committed JDBC Connection

将实体加载到二级缓存后,缓存将为随后的所有调用提供服务,从而绕过数据库。

更新实体

只读缓存条目不允许更新。 任何此类尝试最终都会引发异常:

@Test
public void testReadOnlyEntityUpdate() {try {LOGGER.info("Read-only cache entries cannot be updated");doInTransaction(session -> {Repository repository = (Repository) session.get(Repository.class, 1L);repository.setName("High-Performance Hibernate");});} catch (Exception e) {LOGGER.error("Expected", e);}
}

运行此测试将生成以下输出:

--Read-only cache entries cannot be updatedSELECT readonlyca0_.id   AS id1_2_0_,readonlyca0_.NAME AS name2_2_0_
FROM   repository readonlyca0_
WHERE  readonlyca0_.id = 1 UPDATE repository
SET    NAME = 'High-Performance Hibernate'
WHERE  id = 1 --JdbcTransaction - rolled JDBC Connection--ERROR Expected
--java.lang.UnsupportedOperationException: Can't write to a readonly object

因为只读缓存实体实际上是不可变的,所以最好将它们赋予 Hibernate特有的@Immutable批注。

删除实体

同时删除关联的实体时,也会删除只读缓存条目:

@Test
public void testReadOnlyEntityDelete() {LOGGER.info("Read-only cache entries can be deleted");doInTransaction(session -> {Repository repository = (Repository) session.get(Repository.class, 1L);assertNotNull(repository);session.delete(repository);});doInTransaction(session -> {Repository repository = (Repository) session.get(Repository.class, 1L);assertNull(repository);});
}

生成以下输出:

--Read-only cache entries can be deletedSELECT readonlyca0_.id   AS id1_2_0_,readonlyca0_.NAME AS name2_2_0_
FROM   repository readonlyca0_
WHERE  readonlyca0_.id = 1;DELETE FROM repository
WHERE  id = 1--JdbcTransaction - committed JDBC ConnectionSELECT readonlyca0_.id   AS id1_2_0_,readonlyca0_.NAME AS name2_2_0_
FROM   repository readonlyca0_
WHERE  readonlyca0_.id = 1; --JdbcTransaction - committed JDBC Connection

PersistenceContext使移除 实体状态转换入队,并且在刷新时 ,数据库和二级缓存都将删除关联的实体记录。

集合缓存

提交实体具有变更组件的集合。

@ElementCollection
@CollectionTable(name="commit_change",joinColumns=@JoinColumn(name="commit_id")
)
private List<Change> changes = new ArrayList<>();

尽管Commit实体作为只读元素进行缓存,但第二级缓存将忽略Change集合。

@Test
public void testCollectionCache() {LOGGER.info("Collections require separate caching");doInTransaction(session -> {Repository repository = (Repository) session.get(Repository.class, 1L);Commit commit = new Commit(repository);commit.getChanges().add(new Change("README.txt", "0a1,5..."));commit.getChanges().add(new Change("web.xml", "17c17..."));session.persist(commit);});doInTransaction(session -> {LOGGER.info("Load Commit from database");Commit commit = (Commit) session.get(Commit.class, 1L);assertEquals(2, commit.getChanges().size());});doInTransaction(session -> {LOGGER.info("Load Commit from cache");Commit commit = (Commit) session.get(Commit.class, 1L);assertEquals(2, commit.getChanges().size());});
}

运行此测试将生成以下输出:

--Collections require separate cachingSELECT readonlyca0_.id   AS id1_2_0_,readonlyca0_.NAME AS name2_2_0_
FROM   repository readonlyca0_
WHERE  readonlyca0_.id = 1;INSERT INTO commit(id, repository_id)
VALUES      (DEFAULT, 1);INSERT INTO commit_change(commit_id, diff, path)
VALUES      (1, '0a1,5...', 'README.txt');       INSERT INTO commit_change(commit_id, diff, path)
VALUES      (1, '17c17...', 'web.xml');--JdbcTransaction - committed JDBC Connection--Load Commit from databaseSELECT readonlyca0_.id   AS id1_2_0_,readonlyca0_.NAME AS name2_2_0_
FROM   repository readonlyca0_
WHERE  readonlyca0_.id = 1;SELECT changes0_.commit_id AS commit_i1_0_0_,changes0_.diff      AS diff2_1_0_,changes0_.path      AS path3_1_0_
FROM   commit_change changes0_
WHERE  changes0_.commit_id = 1 --JdbcTransaction - committed JDBC Connection--Load Commit from cacheSELECT changes0_.commit_id AS commit_i1_0_0_,changes0_.diff      AS diff2_1_0_,changes0_.path      AS path3_1_0_
FROM   commit_change changes0_
WHERE  changes0_.commit_id = 1 --JdbcTransaction - committed JDBC Connection

尽管Commit实体是从缓存中检索的,但Change集合始终是从数据库中获取的。 由于更改也是不可变的,因此我们也希望对其进行缓存,以节省不必要的数据库往返次数。

启用集合缓存支持

默认情况下,不缓存集合,并且要启用此行为,我们必须使用缓存并发策略为它们添加注释:

@ElementCollection
@CollectionTable(name="commit_change",joinColumns=@JoinColumn(name="commit_id")
)
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY
)
private List<Change> changes = new ArrayList<>();

重新运行先前的测试将产生以下输出:

--Collections require separate cachingSELECT readonlyca0_.id   AS id1_2_0_,readonlyca0_.NAME AS name2_2_0_
FROM   repository readonlyca0_
WHERE  readonlyca0_.id = 1;INSERT INTO commit(id, repository_id)
VALUES      (DEFAULT, 1);INSERT INTO commit_change(commit_id, diff, path)
VALUES      (1, '0a1,5...', 'README.txt');       INSERT INTO commit_change(commit_id, diff, path)
VALUES      (1, '17c17...', 'web.xml');--JdbcTransaction - committed JDBC Connection--Load Commit from databaseSELECT readonlyca0_.id   AS id1_2_0_,readonlyca0_.NAME AS name2_2_0_
FROM   repository readonlyca0_
WHERE  readonlyca0_.id = 1;SELECT changes0_.commit_id AS commit_i1_0_0_,changes0_.diff      AS diff2_1_0_,changes0_.path      AS path3_1_0_
FROM   commit_change changes0_
WHERE  changes0_.commit_id = 1 --JdbcTransaction - committed JDBC Connection--Load Commit from cache--JdbcTransaction - committed JDBC Connection

一旦集合被缓存,我们就可以获取Commit实体及其所有Change,而无需访问数据库。

结论

只读实体可以安全地进行缓存,我们可以仅使用第二级缓存来加载整个不可变实体图。 由于高速缓存是直通的 ,因此从数据库中获取实体时将对其进行高速缓存。 只读缓存不是直写的,因为持久存储实体只会在新的数据库行中实现,而不会传播到缓存中。

  • 代码可在GitHub上获得 。

翻译自: https://www.javacodegeeks.com/2015/04/how-does-hibernate-read_only-cacheconcurrencystrategy-work.html

Hibernate READ_ONLY CacheConcurrencyStrategy如何工作相关推荐

  1. Hibernate READ_WRITE CacheConcurrencyStrategy如何工作

    介绍 在我以前的文章中,我介绍了NONSTRICT_READ_WRITE二级缓存并发机制. 在本文中,我将使用READ_WRITE策略继续本主题. 直写式缓存 NONSTRICT_READ_WRITE ...

  2. HibernateNONSTRICT_READ_WRITE CacheConcurrencyStrategy如何工作

    介绍 在我以前的文章中 ,我介绍了READ_ONLY CacheConcurrencyStrategy ,这是不可变实体图的显而易见的选择. 当高速缓存的数据可变时,我们需要使用读写高速缓存策略,本文 ...

  3. 休眠NONSTRICT_READ_WRITE CacheConcurrencyStrategy如何工作

    介绍 在我以前的文章中 ,我介绍了READ_ONLY CacheConcurrencyStrategy ,这是不可变实体图的显而易见的选择. 当缓存的数据可变时,我们需要使用读写缓存策略,本文将介绍N ...

  4. Hibernate Collection Cache如何工作

    介绍 之前,我描述了Hibernate用于存储实体的二级缓存条目结构. 除了实体,Hibernate还可以存储实体关联,本文将阐明集合缓存的内部工作原理. 领域模型 对于即将进行的测试,我们将使用以下 ...

  5. Hibernate查询缓存如何工作

    介绍 既然我已经介绍了实体和集合缓存,现在该研究查询缓存的工作原理了. 查询缓存与实体严格相关,它在搜索条件和满足该特定查询过滤器的实体之间绘制关联. 像其他Hibernate功能一样,查询缓存也不像 ...

  6. Struts1、Struts2、Hibernate、Spring框架工作原理介绍

    Struts1工作原理 Struts1工作原理图 1 .初始化: struts 框架的总控制器 ActionServlet 是一个 Servlet ,它在 web.xml 中配置成自动启动的 Serv ...

  7. Hibernate面试问题与解答

    Hibernate面试问题与解答 Hibernate是Java应用程序中使用最广泛的ORM工具之一.它在企业应用程序中用于数据库操作.所以我决定写一篇关于的帖子 hibernate面试问题,在面试前刷 ...

  8. Hibernate EHCache - Hibernate二级缓存

    Hibernate EHCache - Hibernate二级缓存 欢迎使用Hibernate二级缓存示例教程.今天我们将研究Hibernate EHCache,它是最受欢迎的Hibernate二级缓 ...

  9. HibernateEHCache –Hibernate二级缓存

    Welcome to the Hibernate Second Level Cache Example Tutorial. Today we will look into Hibernate EHCa ...

最新文章

  1. 如何修改html页眉页脚,如何使用标准页眉和页脚修改/更新一组html文件
  2. JAVA是如何传递参数的?是传值(by value)?还是传地址(by reference)?
  3. TCP/IP学习笔记(五)TCP的保活定时器
  4. 饥荒联机版服务器显示错误,小白求问 搭服务器出现这种情况是怎么回事
  5. LeetCode 133. 克隆图(图的BFS/DFS)
  6. CubeMX创建stm32项目及遇到的问题
  7. 智慧楼宇物联网公司En-trak获Pre-A 轮融资
  8. Java学习日记之 Java-IO流
  9. LCS算法 文本对比的实现
  10. 计算机网络有个红叉怎么办,笔记本电脑右下角网络出现红叉的解决办法
  11. 十以内加法速算游戏设计
  12. 《阴阳师》RPG游戏体验报告
  13. python 余弦值_余弦相似度
  14. 理想国pandas练习题4
  15. JMeter性能测试之使用CSV文件参数化
  16. 如何快速干净彻底的卸载Oracle11g
  17. 常见功能测试点的测试用例大全
  18. vs编写java_vs code编写java
  19. 如何设置微信公众号粉丝关注后自动推送小程序
  20. 数字工厂生产监控可视化决策平台,打造智能制造新时代

热门文章

  1. Java中的binarySearch方法
  2. Oracle数据库基本概念理解(1)
  3. SSM整合简单登录案例
  4. 异步任务---SpringBoot
  5. zookeeper 屁民
  6. 如何在Intellij IDEA中集成Gitlab
  7. 微信小程序父级之间的传值_微信小程序自定义组件封装及父子间组件传值的方法...
  8. linux 文档操作,Linux学习之文档操作
  9. java线程——什么是线程?
  10. Redis两种客户端:lettuce和Jedis的区别