簡體   English   中英

何時以及如何使用 hibernate 二級緩存?

[英]When and how to use hibernate second level cache?

我無法理解 hibernate 何時命中二級緩存以及何時使緩存無效。

這是我目前的理解:

  • 二級緩存存儲會話之間的實體,scope是SessionFactory
  • 您必須告訴要緩存哪些實體,默認情況下不會緩存任何實體
  • 查詢緩存將查詢結果存儲在緩存中。

我不明白的是

  • hibernate 什么時候命中這個緩存?
  • 假設我已經設置了二級緩存但沒有設置查詢緩存。 我想緩存我的客戶,其中有 50000 個。 我可以通過哪些方式從緩存中檢索客戶?
  • 我假設我可以通過緩存中的 id 獲取它們。 這很容易,但也不值得緩存。 但是,如果我想對所有客戶進行一些計算怎么辦? 假設我想顯示客戶列表,那么我將如何訪問他們?
  • 如果查詢緩存被禁用,我將如何獲得所有客戶?
  • 如果有人更新了其中一個客戶會怎樣?
    • 該客戶會在緩存中失效還是所有客戶都會失效?

還是我認為緩存完全錯誤? 在那種情況下,二級緩存的更合適用途是什么? hibernate 文檔根本不清楚緩存在現實中是如何工作的。 只有關於如何設置它的說明。

更新:所以我開始明白二級緩存(沒有查詢緩存)對於通過 id 加載數據很有用。 例如,我有用戶 object,我想檢查 web 應用程序中每個請求的權限。 這是通過在二級緩存中緩存用戶來減少數據庫訪問的好案例嗎? 就像我將用戶 ID 存儲在 session 中一樣,或者當我需要檢查權限的任何地方時,我會通過它的 ID 加載用戶並檢查權限。

首先,讓我們談談進程級緩存(或在 Hibernate 中稱之為二級緩存)。 為了使它工作,你應該

  1. 配置緩存提供程序
  2. 告訴 hibernate 要緩存哪些實體(如果使用這種映射,則在 hbm.xml 文件中)。

您告訴緩存提供者它應該存儲多少對象以及何時/為什么應該使它們無效。 因此,假設您有 Book 和 Author 實體,每次從 DB 中獲取它們時,只會從實際 DB 中選擇那些不在緩存中的實體。 這顯着提高了性能。 在以下情況下很有用:

  • 您僅通過 Hibernate 寫入數據庫(因為它需要一種方法來知道何時更改或使緩存中的實體無效)
  • 你經常閱讀物品
  • 您只有一個節點,並且沒有復制。 否則,您將需要復制緩存本身(使用 JGroups 之類的分布式緩存),這會增加更多復雜性,並且它的擴展性不如無共享應用程序。

那么緩存什么時候起作用呢?

  • 當您session.get()session.load()時,之前選擇並駐留在緩存中的 object。 緩存是一種存儲,其中 ID 是鍵,屬性是值。 因此,只有當有可能通過 ID 進行搜索時,您才能消除對數據庫的訪問。
  • 當您的關聯是延遲加載時(或使用選擇而不是連接進行急切加載)

但它在以下情況下不起作用:

  • 如果你沒有 select 的 ID。 再次 - 二級緩存將實體 ID 的 map 存儲到其他屬性(它實際上並不存儲對象,而是數據本身),因此如果您的查找如下所示: from Authors where name =:name ,那么您不需要t命中緩存。
  • 當您使用 HQL 時(即使您使用where id =? )。
  • 如果在您的映射中設置fetch="join" ,這意味着加載關聯連接將在任何地方使用,而不是單獨的 select 語句。 進程級緩存僅在使用fetch="select"時才對子對象起作用。
  • 即使您有fetch="select"但是在 HQL 中您使用連接到 select 關聯 - 這些連接將立即發出,它們將覆蓋您在 hbm.xml 或注釋中指定的任何內容。

現在,關於查詢緩存。 您應該注意,它不是一個單獨的緩存,它是對進程級緩存的補充。 假設您有一個 Country 實體。 它是 static,所以你知道當你說from Country時,每次都會有相同的結果集。 這是查詢緩存的完美候選者,它本身會存儲一個ID列表,當您下次使用 select 所有國家/地區時,它會將此列表返回到進程級緩存,而后者又會返回每個 ID 的對象因為這些對象已經存儲在二級緩存中。 每次與實體相關的任何內容發生更改時,查詢緩存都會失效。 因此,假設您from Authors配置為放入查詢緩存中。 由於作者經常更改,因此它不會有效。 因此,您應該只對更多或更少的 static 數據使用查詢緩存。

  • 二級緩存是鍵值存儲。 它僅在您通過 id 獲取實體時才有效
  • 當通過 hibernate 更新/刪除實體時,每個實體的二級緩存無效/更新。 如果數據庫以不同的方式更新,它不會失效。
  • 對於查詢(例如客戶列表),使用查詢緩存。

實際上,擁有一個鍵值對分布式緩存很有用——這就是 memcached,它為 facebook、twitter 等提供支持。 但是,如果您沒有按 id 進行查找,那么它不會很有用。

遲到了,但想系統地回答許多開發人員提出的這些問題。

在這里一一回答你的問題是我的答案。

Q. hibernate 什么時候命中這個緩存?

A.一級緩存Session object相關聯。 二級緩存Session 工廠 object相關聯。 如果第一級沒有找到object,則檢查第二級。

問:假設我設置了二級緩存,但沒有設置查詢緩存。 我想緩存我的客戶,他們有 50000 個。 我可以通過哪些方式從緩存中檢索客戶?

答:您在更新中得到了答案。 此外,查詢緩存僅存儲 object 的 ID 列表,而這些對象 w.r.t 的 ID 存儲在相同的二級緩存中。 因此,如果您啟用查詢緩存,您將使用相同的資源。 整齊吧?

問:我假設我可以通過 id 從緩存中獲取它們。 這很容易,但也不值得緩存。 但是,如果我想對所有客戶進行一些計算怎么辦。 假設我想顯示客戶列表,那么我將如何訪問他們?

A. 以上回答。

問:如果禁用查詢緩存,我將如何獲得所有客戶?

A. 以上回答。

問:如果有人更新了其中一位客戶會發生什么? 該客戶會在緩存中失效還是所有客戶都會失效?

A. Hibernate 不知道,但您可以使用其他第三方 IMDG / 分布式緩存來實現hibernate 二級緩存並使它們失效。 例如TayzGrid就是這樣一種產品,我猜還有更多。

Hibernate 二級緩存理解和實現有點棘手。 根據您的問題,我們可以說以下內容:

Hibernate 什么時候命中這個緩存?

正如您所建議的,僅在 L1 緩存之后才查詢 Hibernate L2 緩存(如果啟用;默認情況下未打開)。 這是一個鍵值緩存,其數據跨多個會話保留。

假設我設置了二級緩存,但沒有設置查詢緩存。 我想緩存我的客戶,他們有 50000 個。 我可以通過哪些方式從緩存中檢索客戶?

查詢緩存最適合此用例,因為客戶數據是 static 並從關系數據庫中檢索。

如果有人更新了其中一位客戶會發生什么? 該客戶會在緩存中失效還是所有客戶都會失效?

這取決於您使用的特定 Hibernate 緩存策略。 Hibernate 實際上有四種不同的緩存策略:

READ_ONLY :對象在緩存中一次不會更改。

NONSTRICT_READ_WRITE :對象在相應的數據庫條目更新后(最終)發生變化; 這保證了最終的一致性。

READ_WRITE :對象在相應的數據庫條目更新后(立即)發生變化; 這通過使用“軟”鎖保證了強一致性。

TRANSACTIONAL :使用分布式 XA 事務更改對象,確保數據完整性; 這可以保證完全成功或回滾所有更改。 但是,在所有這四種情況下,更新單個數據庫條目不會使緩存中的整個客戶列表無效。 Hibernate 比這聰明一點:)

To learn more about how L2 caching works in Hibernate, you can check out the article “What is the Hibernate L2 cache,” or the in-depth article Caching in Hibernate with Redis

我的工作二級緩存代碼。

hibernate.cfg.xml

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/practice</property>
        <property name="connection.user">root</property>
        <property name="connection.password">Welcome123#</property>
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="hbm2ddl.auto">update</property>

        <property name="cache.use_second_level_cache">true</property>
        <property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
        <property name="cache.ehcache.missing_cache_strategy">create</property>

        <mapping class="com.oracle.dto.BankAccount"/>
        <mapping class="com.oracle.dto.Citizen"/>



    </session-factory>
</hibernate-configuration>

package com.oracle.dto;

import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import javax.persistence.*;
import java.util.Date;

@Entity
@Data
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class BankAccount {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO,generator = "bid")
    @SequenceGenerator(name = "bid",sequenceName = "b_id",initialValue = 2000,allocationSize = 1)
    @Setter(AccessLevel.NONE)
    @Column(name="bank_id",updatable = false,nullable = false)
    private Long bId;
    private Integer branchCode;
    private Double accountBalance;
    @Temporal(TemporalType.DATE)
    @Column(name="account_opening_date",updatable = false,nullable = false)
    private Date accountOpeningDate;
}

主 Class

package com.oracle.main;

import com.oracle.dto.BankAccount;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class CacheDriver {
    public static void main(String[] args) {
        SessionFactory sf=new Configuration().configure().buildSessionFactory();
        Session session1 = sf.openSession();
        BankAccount account1 = session1.get(BankAccount.class,2000l);
        System.out.println(account1);
        session1.close();
        Session session2 = sf.openSession();
        BankAccount account2 = session2.get(BankAccount.class,2000l);
        System.out.println(account2);
        session1.close();
        sf.close();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM