Hibernate READ_ONLY CacheConcurrencyStrategy如何工作
介紹
正如我前面所解釋的 ,企業的高速緩存需要勤奮。 由于數據在數據庫( 記錄系統 )和緩存層之間重復,因此我們需要確保兩個單獨的數據源不會分開。
如果緩存的數據是不可變的(數據庫和緩存都無法修改它),我們可以安全地對其進行緩存,而不必擔心任何一致性問題。 只讀數據始終是應用程序級緩存的理想選擇,可以在不放松一致性保證的情況下提高讀取性能。
只讀二級緩存
為了測試只讀二級緩存策略,我們將使用以下域模型:
存儲庫是根實體,是任何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 ConnectionPersistenceContext使移除 實體狀態轉換入隊,并且在刷新時 ,數據庫和二級緩存都將刪除關聯的實體記錄。
集合緩存
提交實體具有變更組件的集合。
@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如何工作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为p20屏幕尺寸多大(华为p20pro
- 下一篇: 电脑硬盘完全清零(电脑硬盘清空)