![](/img/trans.png)
[英]How to use second level cache for lazy loaded collections in Hibernate?
[英]When and how to use hibernate second level cache?
我無法理解 hibernate 何時命中二級緩存以及何時使緩存無效。
這是我目前的理解:
我不明白的是
還是我認為緩存完全錯誤? 在那種情況下,二級緩存的更合適用途是什么? hibernate 文檔根本不清楚緩存在現實中是如何工作的。 只有關於如何設置它的說明。
更新:所以我開始明白二級緩存(沒有查詢緩存)對於通過 id 加載數據很有用。 例如,我有用戶 object,我想檢查 web 應用程序中每個請求的權限。 這是通過在二級緩存中緩存用戶來減少數據庫訪問的好案例嗎? 就像我將用戶 ID 存儲在 session 中一樣,或者當我需要檢查權限的任何地方時,我會通過它的 ID 加載用戶並檢查權限。
首先,讓我們談談進程級緩存(或在 Hibernate 中稱之為二級緩存)。 為了使它工作,你應該
您告訴緩存提供者它應該存儲多少對象以及何時/為什么應該使它們無效。 因此,假設您有 Book 和 Author 實體,每次從 DB 中獲取它們時,只會從實際 DB 中選擇那些不在緩存中的實體。 這顯着提高了性能。 在以下情況下很有用:
那么緩存什么時候起作用呢?
session.get()
或session.load()
時,之前選擇並駐留在緩存中的 object。 緩存是一種存儲,其中 ID 是鍵,屬性是值。 因此,只有當有可能通過 ID 進行搜索時,您才能消除對數據庫的訪問。但它在以下情況下不起作用:
from Authors where name =:name
,那么您不需要t命中緩存。where id =?
)。fetch="join"
,這意味着加載關聯連接將在任何地方使用,而不是單獨的 select 語句。 進程級緩存僅在使用fetch="select"
時才對子對象起作用。fetch="select"
但是在 HQL 中您使用連接到 select 關聯 - 這些連接將立即發出,它們將覆蓋您在 hbm.xml 或注釋中指定的任何內容。 現在,關於查詢緩存。 您應該注意,它不是一個單獨的緩存,它是對進程級緩存的補充。 假設您有一個 Country 實體。 它是 static,所以你知道當你說from Country
時,每次都會有相同的結果集。 這是查詢緩存的完美候選者,它本身會存儲一個ID列表,當您下次使用 select 所有國家/地區時,它會將此列表返回到進程級緩存,而后者又會返回每個 ID 的對象因為這些對象已經存儲在二級緩存中。 每次與實體相關的任何內容發生更改時,查詢緩存都會失效。 因此,假設您from Authors
配置為放入查詢緩存中。 由於作者經常更改,因此它不會有效。 因此,您應該只對更多或更少的 static 數據使用查詢緩存。
實際上,擁有一個鍵值對分布式緩存很有用——這就是 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.