1.什麼是緩存?java
緩存是介於物理數據源與應用程序之間,是對數據庫中的數據複製一份臨時放在內存中的容器,其做用是爲了減小應用程序對物理數據源訪問的次數,從而提升了應用程序的運行性能。Hibernate在進行讀取數據的時候,根據緩存機制在相應的緩存中查詢,若是在緩存中找到了須要的數據(咱們把這稱作「緩存命 中"),則就直接把命中的數據做爲結果加以利用,避免了大量發送SQL語句到數據庫查詢的性能損耗。spring
緩存策略提供商:sql
提供了HashTable緩存,EHCache,OSCache,SwarmCache,jBoss Cathe2,這些緩存機制,其中EHCache,OSCache是不能用於集羣環境(Cluster Safe)的,而SwarmCache,jBoss Cathe2是能夠的。HashTable緩存主要是用來測試的,只能把對象放在內存中,EHCache,OSCache能夠把對象放在內存(memory)中,也能夠把對象放在硬盤(disk)上(爲何放到硬盤上?上面解釋了)。數據庫
Hibernate緩存分類:緩存
1、Session緩存(又稱做事務緩存):Hibernate內置的,不能卸除。session
緩存範圍:緩存只能被當前Session對象訪問。緩存的生命週期依賴於Session的生命週期,當Session被關閉後,緩存也就結束生命週期。多線程
2、SessionFactory緩存(又稱做應用緩存):使用第三方插件,可插拔。併發
緩存範圍:緩存被應用範圍內的全部session共享,不一樣的Session能夠共享。這些session有多是併發訪問緩存,所以必須對緩存進行更新。緩存的生命週期依賴於應用的生命週期,應用結束時,緩存也就結束了生命週期,二級緩存存在於應用程序範圍。app
一級緩存:ide
Hibernate一些與一級緩存相關的操做(時間點):
數據放入緩存:
1. save()。當session對象調用save()方法保存一個對象後,該對象會被放入到session的緩存中。
2. get()和load()。當session對象調用get()或load()方法從數據庫取出一個對象後,該對象也會被放入到session的緩存中。
3. 使用HQL和QBC等從數據庫中查詢數據。
例如:數據庫有一張表以下:
使用get()或load()證實緩存的存在:
public class Client { public static void main(String[] args) { Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tx = null; try { /*開啓一個事務*/ tx = session.beginTransaction(); /*從數據庫中獲取id="402881e534fa5a440134fa5a45340002"的Customer對象*/ Customer customer1 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002"); System.out.println("customer.getUsername is"+customer1.getUsername()); /*事務提交*/ tx.commit(); System.out.println("-------------------------------------"); /*開啓一個新事務*/ tx = session.beginTransaction(); /*從數據庫中獲取id="402881e534fa5a440134fa5a45340002"的Customer對象*/ Customer customer2 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002"); System.out.println("customer2.getUsername is"+customer2.getUsername()); /*事務提交*/ tx.commit(); System.out.println("-------------------------------------"); /*比較兩個get()方法獲取的對象是不是同一個對象*/ System.out.println("customer1 == customer2 result is "+(customer1==customer2)); } catch (Exception e) { if(tx!=null) { tx.rollback(); } } finally { session.close(); } } }
程序控制臺輸出結果:
Hibernate:
select
customer0_.id as id0_0_,
customer0_.username as username0_0_,
customer0_.balance as balance0_0_
from
customer customer0_
where
customer0_.id=?
customer.getUsername islisi
-------------------------------------
customer2.getUsername islisi
-------------------------------------
customer1 == customer2 result is true
其原理是:在同一個Session裏面,第一次調用get()方法, Hibernate先檢索緩存中是否有該查找對象,發現沒有,Hibernate發送SELECT語句到數據庫中取出相應的對象,而後將該對象放入緩存中,以便下次使用,第二次調用get()方法,Hibernate先檢索緩存中是否有該查找對象,發現正好有該查找對象,就從緩存中取出來,再也不去數據庫中檢索,沒有再次發送select語句。
數據從緩存中清除:
1. evit()將指定的持久化對象從緩存中清除,釋放對象所佔用的內存資源,指定對象從持久化狀態變爲脫管狀態,從而成爲遊離對象。
2. clear()將緩存中的全部持久化對象清除,釋放其佔用的內存資源。
其餘緩存操做:
1. contains()判斷指定的對象是否存在於緩存中。
2. flush()刷新緩存區的內容,使之與數據庫數據保持同步。
二級緩存:
@Testpublic void testCache2() { Session session1 = sf.openSession();//得到Session1session1.beginTransaction(); Category c = (Category)session1.load(Category.class, 1); System.out.println(c.getName()); session1.getTransaction().commit(); session1.close(); Session session2 = sf.openSession();//得到Session2session2.beginTransaction(); Category c2 = (Category)session2.load(Category.class, 1); System.out.println(c2.getName()); session2.getTransaction().commit(); session2.close(); }
當咱們重啓一個Session,第二次調用load或者get方法檢索同一個對象的時候會從新查找數據庫,會發select語句信息。
緣由:一個session不能取另外一個session中的緩存。
性能上的問題:假如是多線程同時去取Category這個對象,load一個對象,這個對像原本能夠放到內存中的,但是因爲是多線程,是分佈在不一樣的session當中的,因此每次都要從數據庫中取,這樣會帶來查詢性能較低的問題。
解決方案:使用二級緩存。
1.什麼是二級緩存?
SessionFactory級別的緩存,能夠跨越Session存在,能夠被多個Session所共享。
2.適合放到二級緩存中:
(1)常常被訪問
(2)改動不大
(3)數量有限
(4)不是很重要的數據,容許出現偶爾併發的數據。
這樣的數據很是適合放到二級緩存中的。
用戶的權限:用戶的數量不大,權限很少,不會常常被改動,常常被訪問。
例如組織機構。
思考:什麼樣的類,裏面的對象才適合放到二級緩存中?
改動頻繁,類裏面對象特別多,BBS好多帖子,這些帖子20000多條,哪些放到緩存中,不能肯定。除非你肯定有一些常常被訪問的,數據量並不大,改動很是少,這樣的數據很是適合放到二級緩存中的。
3.二級緩存實現原理:
Hibernate如何將數據庫中的數據放入到二級緩存中?注意,你能夠把緩存看作是一個Map對象,它的Key用於存儲對象OID,Value用於存儲POJO。首先,當咱們使用Hibernate從數據庫中查詢出數據,獲取檢索的數據後,Hibernate將檢索出來的對象的OID放入緩存中key 中,而後將具體的POJO放入value中,等待下一次再次向數據查詢數據時,Hibernate根據你提供的OID先檢索一級緩存,如有且配置了二級緩存,則檢索二級緩存,若是尚未則才向數據庫發送SQL語句,而後將查詢出來的對象放入緩存中。
4.使用二級緩存
(1)打開二級緩存:
爲Hibernate配置二級緩存:
在主配置文件中hibernate.cfg.xml :
<!-- 使用二級緩存 -->
<!-- 使用二級緩存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <!--設置緩存的類型,設置緩存的提供商--> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
或者當hibernate與Spring整合後直接配到Spring配置文件applicationContext.xml中
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources"> <list> <value>com/lp/ecjtu/model/Employee.hbm.xml</value> <value>com/lp/ecjtu/model/Department.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.OracleDialect hibernate.hbm2ddl.auto=update hibernate.show_sql=true hibernate.format_sql=true hibernate.cache.use_second_level_cache=true hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider hibernate.generate_statistics=true </value> </property> </bean>
(2)配置ehcache.xml
<?xml version="1.0" encoding="UTF-8"?><ehcache> <!-- 緩存到硬盤的路徑 --> <diskStore path="d:/ehcache"></diskStore> <!-- 默認設置 maxElementsInMemory : 在內存中最大緩存的對象數量。 eternal : 緩存的對象是否永遠不變。 timeToIdleSeconds :能夠操做對象的時間。 timeToLiveSeconds :緩存中對象的生命週期,時間到後查詢數據會從數據庫中讀取。 overflowToDisk :內存滿了,是否要緩存到硬盤。 --> <defaultCache maxElementsInMemory="200" eternal="false" timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache> <!-- 指定緩存的對象。 下面出現的的屬性覆蓋上面出現的,沒出現的繼承上面的。 --> <cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false" timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache></ehcache>
(3)使用二級緩存須要在實體類中加入註解:
須要ehcache-1.2.3.jar包:
還須要 commons_loging1.1.1.jar包
在實體類中經過註解能夠配置實用二級緩存:
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
Load默認使用二級緩存,就是當查一個對象的時候,它先會去二級緩存裏面去找,若是找到了就不去數據庫中查了。
Iterator默認的也會使用二級緩存,有的話就不去數據庫裏面查了,不發送select語句了。
List默認的往二級緩存中加數據,假若有一個query,把數據拿出來以後會放到二級緩存,可是執行查詢的時候不會到二級緩存中查,會在數據庫中查。緣由每一個query中查詢條件不同。
(4)也能夠在須要被緩存的對象中hbm文件中的<class>標籤下添加一個<cache>子標籤:
<hibernate-mapping> <class name="com.suxiaolei.hibernate.pojos.Order" table="orders"> <cache usage="read-only"/> <id name="id" type="string"> <column name="id"></column> <generator class="uuid"></generator> </id> <property name="orderNumber" column="orderNumber" type="string"></property> <property name="cost" column="cost" type="integer"></property> <many-to-one name="customer" class="com.suxiaolei.hibernate.pojos.Customer" column="customer_id" cascade="save-update"> </many-to-one> </class> </hibernate-mapping>
存在一對多的關係,想要在在獲取一方的時候將關聯的多方緩存起來,須要再集合屬性下添加<cache>子標籤,這裏須要將關聯的對象的 hbm文件中必須在存在<class>標籤下也添加<cache>標籤,否則Hibernate只會緩存OID。
<hibernate-mapping> <class name="com.suxiaolei.hibernate.pojos.Customer" table="customer"> <!-- 主鍵設置 --> <id name="id" type="string"> <column name="id"></column> <generator class="uuid"></generator> </id> <!-- 屬性設置 --> <property name="username" column="username" type="string"></property> <property name="balance" column="balance" type="integer"></property> <set name="orders" inverse="true" cascade="all" lazy="false" fetch="join"> <cache usage="read-only"/> <key column="customer_id" ></key> <one-to-many class="com.suxiaolei.hibernate.pojos.Order"/> </set> </class> </hibernate-mapping>