Hibernate的二級緩存 理解緩存定義: 緩存(Cache):電腦領域非常通用的概念。它介於應用程式和永久性數據存儲源(如硬碟上的文件或者資料庫)之間,其作用是降低應用程式直接讀寫永久性數據存儲源的頻率,從而提高應用的運行性能。緩存中的數據是數據存儲源中數據的拷貝。緩存的物理介質通常是記憶體。 ...
Hibernate的二級緩存
- 理解緩存定義:
- 緩存(Cache):電腦領域非常通用的概念。它介於應用程式和永久性數據存儲源(如硬碟上的文件或者資料庫)之間,其作用是降低應用程式直接讀寫永久性數據存儲源的頻率,從而提高應用的運行性能。緩存中的數據是數據存儲源中數據的拷貝。緩存的物理介質通常是記憶體。
- 理解二級緩存的定義:
- Hibernate中提供了兩個級別的緩存
- 一級緩存是Session級別的緩存,它是屬於事務範圍的緩存。這一級別的緩存是由Hibernate管理的,一般情況下無需進行干預。
- 二級緩存是SessionFactory級別的緩存,它是屬於進程範圍的緩存。
- Hibernate的SessionFactory緩存可以分為兩類:
- 內置緩存:Hibernate自帶的,不可拆卸。通常在Hibernate的初始化階段,Hibernate會把映射元數據和預定義的SQL語句放到SessionFactory的緩存中,映射元數據是映射文件中數據(*.hbm.xml文件中的數據)的複製,該內置緩存是只讀的。
- 外置緩存(二級緩存):一個可排至的緩存插件,在預設情況下,SessionFactory不會啟用這個緩存插件。外置緩存中的數據是資料庫數據的複製,外置緩存的物理介質可以是記憶體或硬碟。
- Hibernate中提供了兩個級別的緩存
- 使用Hibernate的二級緩存:
- 適合放入二級緩存中的數據:
- 很少被修改。
- 不是很重要的數據,允許出現偶爾的併發問題。
- 不適合放入二級緩存中的數據:
- 經常被修改。
- 財務數據,絕對不允許出現併發問題。
- 與其他應用程式共用的數據。
- 適合放入二級緩存中的數據:
- Hibernate的二級緩存的架構
- 二級緩存的併發訪問策略:
- 兩個併發的事務同時訪問吃就吃的緩存的相同數據時,也有可能出現各類併發問題。
- 二級緩存可以設定一下4中類型的併發訪問策略,每一種訪問策略對應一種事務的隔離級別。
- 非嚴格讀寫(nonstrict-read-wirte):不保證緩存與資料庫中數據的一致性。提供Read Uncommited事務隔離級別。對於極少被修改,而且允許臟讀的數據,可以採用這種策略。
- 讀寫型read-write):提供Read Committed數據隔離級別。對於經常讀但是很少被修改的數據,可以採用這種隔離烈性,因為它可以防止臟讀。
- 事務型(transaction):僅僅在受管理環境下使用。它提供了Repeatable Read 事務隔離級別。對於經常讀但是很少被修改的數據,可以採用這種隔離級別,因為它可以防止臟讀和不可以重覆讀。
- 只讀型(read-only):提供Serializable數據隔離級別。對於從來不會被修改的數據,可以採用這種訪問策略。
- 管理Hibernate的二級緩存
- Hibernate的二級緩存是進程或集群範圍內的緩存。
- 二級緩存是可配置的插件,Hibernate允許選用以下類型的緩存插件:
- EHCache:可作為進程分為內的緩存,存放數據的物理介質可以是記憶體或硬碟,對Hibernate的查詢緩存提供了支持。
- OSCache:可作為進程範圍內的緩存,存放數據的物理介質可以是記憶體或硬碟,提供了豐富的緩存數據過期策略,對Hibernate的查詢緩存提供了支持。
- SwarmCache:可作為集群範圍內的緩存,但是不支持Hibernate的查詢緩存。
- JBossCache:可作為集群範圍內的緩存,支持Hibernate的查詢緩存。
- 4種緩存插件的併發訪問策略
緩存插件 | read-only | nonstrict-read-write | read-write | transaction |
EHCache | √ | √ | √ | |
OSCache | √ | √ | √ | |
SwarmCache | √ | √ | ||
JBossCache | √ | √ |
- 使用Hibernate的二級緩存的步驟:
- 加入二級緩存插件的jar包及配置文件。
- backport-util-concurrent.jar
- commons-logging.jar
- ehcache-1.5.0.jar
- ehcache.xml
- 配置hibernate.cfg.xml
- 配置啟動Hibernate的二級緩存
-
<!-- 啟用二級緩存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
-
- 配置啟動Hibernate的二級緩存
- 加入二級緩存插件的jar包及配置文件。
-
-
- 配置二級緩存的提供商
- <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
- 配置那些類使用二級緩存
-
<!-- 配置那個類使用二級緩存 -->
<class-cache usage="read-only" class="cn.hibernate3.demo4.Customer"/>
-
- 配置二級緩存的提供商
-
- 二級緩存的示例:證明二級緩存的存在
- 配置演示環境:實體類,對應的映射文件及核心配置文件
- 實體類:
- Customer.java
package cn.hibernate3.demo4; import java.io.Serializable; import java.util.HashSet; /** * 客戶實體 */ import java.util.Set; public class Customer implements Serializable{ private Integer cid; private String cname; //一個客戶有多個訂單 private Set<Order> orders = new HashSet<Order>(); public Integer getCid() { return cid; } public void setCid(Integer cid) { this.cid = cid; } public String getCname() { return cname; } public void setCname(String cname) { this.cname = cname; } public Set<Order> getOrders() { return orders; } public void setOrders(Set<Order> orders) { this.orders = orders; } }
-
-
- Order.java
-
package cn.hibernate3.demo4; import java.io.Serializable; /** * 訂單實體 */ public class Order implements Serializable{ private Integer oid; private String addr; //訂單屬於某一個客戶 private Customer customer ; public Integer getOid() { return oid; } public void setOid(Integer oid) { this.oid = oid; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } }
-
- 映射文件:
- Customer.hbm.xml
- 映射文件:
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入約束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo4.Customer" table="customer" lazy="true"> <!-- 配置唯一標識 --> <id name="cid" column="cid"> <generator class="native"/> </id> <!-- 配置普通屬性 --> <property name="cname" column="cname" type="java.lang.String"/> <!-- 建立映射 --> <!-- 配置集合 --> <!-- set標簽中的name表示關聯對象的屬性名稱 --> <set name="orders" cascade="save-update"> <!-- key標簽中的column用來一對多的多的一方的外鍵 --> <key column="cno"/> <!-- 配置一個one-to-many --> <one-to-many class="cn.hibernate3.demo4.Order" /> </set> </class> </hibernate-mapping>
-
-
- Order.hbm.xml
-
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入約束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.hibernate3.demo4.Order" table="orders"> <!-- 配置唯一標識 --> <id name="oid" column="oid"> <generator class="native"/> </id> <!-- 配置普通屬性 --> <property name="addr" column="addr" type="java.lang.String"/> <!-- 建立映射 --> <!-- many-to-one標簽 屬性: name:關聯對象的屬性名稱。 column:表中外鍵的名稱。 class:關聯對象的全路徑。 --> <many-to-one name="customer" column="cno" class="cn.hibernate3.demo4.Customer"></many-to-one> </class> </hibernate-mapping>
-
- 核心映射文件
<?xml version="1.0" encoding="UTF-8"?> <!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="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- 訪問資料庫的url --> <property name="hibernate.connection.url"> jdbc:mysql:///hibernate_day03 </property> <!-- 用戶名 --> <property name="hibernate.connection.username">root</property> <!-- 密碼 --> <property name="hibernate.connection.password">root</property> <!-- 方言 --> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <!-- C3P0連接池設定--> <!-- 使用c3po連接池 配置連接池提供的供應商--> <property name="connection.provider_class"> org.hibernate.connection.C3P0ConnectionProvider </property> <!--在連接池中可用的資料庫連接的最少數目 --> <property name="c3p0.min_size">5</property> <!--在連接池中所有資料庫連接的最大數目 --> <property name="c3p0.max_size">20</property> <!--設定資料庫連接的過期時間,以秒為單位, 如果連接池中的某個資料庫連接處於空閑狀態的時間超過了timeout時間,就會從連接池中清除 --> <property name="c3p0.timeout">120</property> <!--每3000秒檢查所有連接池中的空閑連接 以秒為單位--> <property name="c3p0.idle_test_period">3000</property> <!-- 可選配置 --> <!-- 顯示SQL --> <property name="hibernate.show_sql">true</property> <!-- 格式化SQL --> <property name="hibernate.format_sql">true</property> <!-- hbm:映射 2:to ddl:create drop alter --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 1—Read uncommitted isolation 2—Read committed isolation 4—Repeatable read isolation 8—Serializable isolation --> <property name="hibernate.connection.isolation">4</property> <property name="hibernate.current_session_context_class">thread</property> <!-- 啟用二級緩存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <!-- 配置使用的二級緩存提供商 --> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <mapping resource="cn/hibernate3/demo4/Customer.hbm.xml" /> <mapping resource="cn/hibernate3/demo4/Order.hbm.xml" /> <!--
<class-cache usage="read-write" class="cn.hibernate3.demo4.Customer"/>
<class-cache usage="read-write" class="cn.hibernate3.demo4.Order"/>
<collection-cache usage="read-write" collection="cn.hibernate3.demo4.Customer.orders"/>
--> </session-factory> </hibernate-configuration>
-
- 測試類
@Test public void demo13(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer1 = (Customer) session.get(Customer.class, 1); System.out.println(customer1.getCname()); tx.commit(); session = HibernateUtils.openSession(); tx = session.beginTransaction(); Customer customer2 = (Customer) session.get(Customer.class, 1); System.out.println(customer2.getCname()); tx.commit(); session.close(); }
-
- 設置Customer類使用二級緩存。其核心配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <!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="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- 訪問資料庫的url --> <property name="hibernate.connection.url"> jdbc:mysql:///hibernate_day03 </property> <!-- 用戶名 --> <property name="hibernate.connection.username">root</property> <!-- 密碼 --> <property name="hibernate.connection.password">root</property> <!-- 方言 --> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <!-- C3P0連接池設定--> <!-- 使用c3po連接池 配置連接池提供的供應商--> <property name="connection.provider_class"> org.hibernate.connection.C3P0ConnectionProvider </property> <!--在連接池中可用的資料庫連接的最少數目 --> <property name="c3p0.min_size">5</property> <!--在連接池中所有資料庫連接的最大數目 --> <property name="c3p0.max_size">20</property> <!--設定資料庫連接的過期時間,以秒為單位, 如果連接池中的某個資料庫連接處於空閑狀態的時間超過了timeout時間,就會從連接池中清除 --> <property name="c3p0.timeout">120</property> <!--每3000秒檢查所有連接池中的空閑連接 以秒為單位--> <property name="c3p0.idle_test_period">3000</property> <!-- 可選配置 --> <!-- 顯示SQL --> <property name="hibernate.show_sql">true</property> <!-- 格式化SQL --> <property name="hibernate.format_sql">true</property> <!-- hbm:映射 2:to ddl:create drop alter --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 1—Read uncommitted isolation 2—Read committed isolation 4—Repeatable read isolation 8—Serializable isolation --> <property name="hibernate.connection.isolation">4</property> <property name="hibernate.current_session_context_class">thread</property> <!-- 啟用二級緩存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <!-- 配置使用的二級緩存提供商 --> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <mapping resource="cn/hibernate3/demo4/Customer.hbm.xml" /> <mapping resource="cn/hibernate3/demo4/Order.hbm.xml" />
<!-- 配置那個類使用二級緩存-->
<class-cache usage="read-write" class="cn.hibernate3.demo4.Customer"/>
<class-cache usage="read-write" class="cn.hibernate3.demo4.Order"/>
<!-- 集合緩存區 -->
<collection-cache usage="read-write" collection="cn.hibernate3.demo4.Customer.orders"/>
</session-factory> </hibernate-configuration>
- 類緩存區的特點:緩存的是對象的散裝的數據。
@Test public void demo13(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer1 = (Customer) session.get(Customer.class, 1); Customer customer2 = (Customer) session.get(Customer.class, 1); System.out.println(customer1==customer2); tx.commit(); session = HibernateUtils.openSession(); tx = session.beginTransaction(); Customer customer3 = (Customer) session.get(Customer.class, 1); Customer customer4 = (Customer) session.get(Customer.class, 1); System.out.println(customer3 == customer4); System.out.println(customer1 == customer3); tx.commit(); session.close(); }
- 集合緩存區的特點:緩存的是對象的id,需要依賴類緩存區的配置。
@Test public void demo13(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer1 = (Customer) session.get(Customer.class, 1); System.out.println(customer1.getOrders().size()); tx.commit(); session = HibernateUtils.openSession(); tx = session.beginTransaction(); Customer custome2 = (Customer) session.get(Customer.class, 1); System.out.println(custome2.getOrders().size()); tx.commit(); session.close(); }
- list()方法和iterate()方法的區別
- list()方法:會向二級緩存中存放數據,但是不會使用二級緩存中的數據。
- 證明:list()方法會向二級緩存中存放數據。
- list()方法:會向二級緩存中存放數據,但是不會使用二級緩存中的數據。
@Test //list()方法會向二級緩存中存放數據,但是不會使用二級緩存中的數據 //證明list()方法會向二級緩存中存放數據 public void demo13(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); List<Customer> list = session.createQuery("from Customer").list();//發送SQL語句 for (Customer customer : list) { System.out.println(customer.getCname()); } tx.commit(); session = HibernateUtils.openSession(); tx = session.beginTransaction(); Customer customer = (Customer) session.get(Customer.class, 1);//不發送SQL語句,從二級緩存中獲取 System.out.println(customer.getCname()); tx.commit(); session.close(); }
-
-
- 證明:list()不會使用二級緩存中的數據。
-
@Test //list()方法會向二級緩存中存放數據,但是不會使用二級緩存中的數據 //證明list()方法不會使用二級緩存中的數據 public void demo13(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); List<Customer> list = session.createQuery("from Customer").list();//發送SQL語句 for (Customer customer : list) { System.out.println(customer.getCname()); } tx.commit(); session = HibernateUtils.openSession(); tx = session.beginTransaction(); list = session.createQuery("from Customer").list();//發送SQL語句 for (Customer customer : list) { System.out.println(customer.getCname()); } tx.commit(); session.close(); }
-
- iterate()方法:
- 和list()方法一樣也能執行查詢操作。
- list()方法執行的SQL語句包含實體類對象的數據表中的所有欄位。
- iterate()方法執行SQL語句中僅包含實體類對應的數據表的id欄位。
- 當遍歷結果集的時候,該方法先到session緩存及二級緩存中查看是否存在特定oid的對象,如果存在,就直接返回該對象,如果不存在,就通過相應的SQL select語句到資料庫中載入特定的實體對象。
- 大多數情況下,應考慮使用list()方法執行查詢操作,iterate()方法僅在滿足以下條件對的場合,可以稍微提高查詢性能:
- 要查詢的數據表中包含大量的欄位。
- 啟用了二級緩存,且二級緩存中可能已經包含了待查詢的對象。
- iterate()方法:
@Test public void demo13(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //發送N+1條SQL去查詢 Iterator<Customer> iterator = session.createQuery("from Customer").iterate(); while(iterator.hasNext()){ Customer customer = iterator.next(); System.out.println(customer.getCname()); } tx.commit(); session = HibernateUtils.openSession(); tx = session.beginTransaction(); iterator = session.createQuery("from Customer").iterate(); while(iterator.hasNext()){ Customer customer = iterator.next(); System.out.println(customer.getCname()); } tx.commit(); session.close(); }
- 一級緩存更新同步到二級緩存及二級緩存配置文件
- 證明一級緩存更新同步到二級緩存
@Test public void demo13(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = (Customer) session.get(Customer.class, 1); customer.setCname("哈哈"); tx.commit(); session = HibernateUtils.openSession(); tx = session.beginTransaction(); Customer customer2 = (Customer) session.get(Customer.class, 1); System.out.println(customer2.getCname()); tx.commit(); session.close(); }
-
- 二級緩存的配置文件
- 更新時間戳區
@Test public void demo13(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = (Customer) session.get(Customer.class, 1); session.createQuery("update Customer set cname='呵呵' where cid= 1").executeUpdate(); tx.commit(); session = HibernateUtils.openSession(); tx = session.beginTransaction(); Customer customer2 = (Customer) session.get(Customer.class, 1); System.out.println(customer2.getCname()); tx.commit(); session.close(); }
- 查詢緩存區
- 比二級緩存功能更加強大,而且查詢緩存必須依賴二級緩存。
- 二級緩存:對類或對象的緩存。
- 查詢緩存:針對類中的屬性的緩存。
- select c.cname from Customer c;
- 查詢緩存的配置
- 前提是二級緩存已經配置完畢。
- 在核心配置文件中配置
- <property name="hibernate.cache.use_query_cache">true</property>
- 編寫代碼的時候,添加如下代碼(詳解示例):
@Test public void demo13(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery("select c.cname from Customer c"); query.setCacheable(true); List<Object> list = query.list(); for (Object object : list) { System.out.println(object); } tx.commit(); session = HibernateUtils.openSession(); tx = session.beginTransaction(); query = session.createQuery("select c.cname from Customer c"); query.setCacheable(true); list = query.list(); for (Object object : list) { System.out.println(object); } tx.commit(); session.close(); }