數據庫的延遲加載機制的應用、緩存管理


一、
1、 延遲加載
延遲加載(load)是Hibernate為提高程序執行效率而提供的一種機制,即只有真正使用該對象的數據時才會創建。
場合一:當用戶要取數據庫的一張表的一個字段,這個字段很可能就是一個字符,總而言之長度是比較短的。

場合二:當用戶要取數據庫的一張表的一個字段的值,而這個值很可能是blob類型,也許存取的是一個很大的視頻文件。

兩種場合的取數據的方法一樣嗎?是用load還是用get方法?


延遲加載的過程:通過代理(Proxy)機制來實現延遲加載。Hibernate從數據庫獲取某一個對象數據時、獲取某一個對象的集合屬性值時,或獲取某一個對象所關聯的另一個對象時,由於沒有使用該對象的數據(除標識符外),Hibernate並不從數據庫加載真正的數據,而只是為該對象創建一個代理對象來代表這個對象,這個對象上的所有屬性都為默認值;只有在真正需要使用該對象的數據時才創建這個真正的對象,真正從數據庫中加載它的數據。

  對象的延遲加載
  對象里的屬性延遲加載
  集合延遲加載


Account  acc=(Account)session.load(Account.class,new Long(1));    //返回一個代理對象
System.out.println(acc.getId);
System.out.prontln(acc.getLonginName());

在只需要Account類的一個引用時,這種延遲加載就很有用。



如果只是訪問對象標示符屬性,就沒有必要初始化代碼。
Account  acc=(Account)session.load(Account.class,new Long(1));    //返回一個代理對象
Order order=new Order();
order.setCreateTime(new Date());
order.setAccount(acc);
Session.save(order);
在這只需要Account實例來創建一個新的Order訂單對象,當調用session.save(order)時,也只需要Account的主標示符值作為外鍵保存到訂單表的對應字段中。這樣就少執行一條select語句,從而提高查詢效率。


Hibernate中默認采用延遲加載的情況主要有以下幾種:
當調用Session上的load()方法加載一個實體時,會采用延遲加載。
當Session加載某個實體時,會對這個實體中的集合屬性值采用延遲加載。(one-to-many)
當Session加載某個實體時,會對這個實體所單端關聯(one-to-one,  many-to-one)的另一個實體對象采用延遲加載。

能夠懶加載的對象都是被改寫過的代理對象,當相關聯的session沒有關閉時,訪問這些懶加載對象(代理對象)的屬性(getId和getClass除外)hibernate會初始化這些代理,或用Hibernate.initialize(proxy)來初始化代理對象;當相關聯的session關閉后,再訪問懶加載的對象將出現異常。




2、 關閉延遲加載
在加載單個實體時,如果不需要延遲加載,就可以使用session的get()方法。
當Session加載某個實體時,不需要對這個實體中的集合屬性值延遲加載,而是要立即加載。這時可以在映射文件中針對 這個集合屬性的配置元素(<set>,<bag>,<list>…)添加屬性lazy=“false”。

當Session加載某個實體時,不需要對這個實體所單端關聯的另一個實體對象延遲加載,就可以在映射文件中對這個單端關聯的配置元素(<one-to-one>,<many-to-one> )添加屬性lazy=“false”。
    注意:one-to-one不能有constrained=true



二、抓取策略
在HQL語句中使用抓取連接查詢,通過寫一條left join fetch 語句把相關聯的兩個實體的數據一次性從數據庫中加載上來。這樣可以在特定情況下(同時需要使用到這兩個實體的數據)減少SQL的數量來提高查詢效率。

通過配置“抓取策略”來直接影響session的get()和load()方法的查詢效果。
1.單端關聯<many-to-one><one-to_one>上的抓取策略。
  可以給單端關聯的映射元素添加fetch屬性。fetch屬性有2個可選值
  select:作為默認值,它的策略是當需要使用到關聯對象的數據時,另外單獨發送一條select語句抓取當前對象的關聯對象的數據。即延遲加載。
  join:它的策略是在同一條select語句使用連接來獲得對象的數據和它關聯對象的數據,此時關聯對象的延遲加載失效。



2.集合屬性上的抓取策略
在集合屬性的映射元素上可以添加fetch屬性,它有3個可選值。
select:作為默認值,它的策略是當需要使用所關聯集合的數據時,另外單獨發送一條select語句抓取當前對象的關聯集合,即延遲加載。
join:在同一條select語句使用連接來獲得對方的關聯集合。此時關聯集合上的lazy會失效。
subselect:另外發送一條查詢語句(或子查詢語句)抓取在前面查詢到的所有實體對象的關聯集合。這個策略對HQL的查詢也起作用。



當fetch為join時,執行左外連接,這個時候,在加載Customer時,Customer所對應的Order值全部被加載到緩存中,如果Order中沒有大數據,這個策略是一個不錯的選擇。
當fetch為subselect時,針對in有效,如果為select時,from Customer where id in(1,2,3),hibernate會把ID取出來,逐一的去取Order的值,效率比較低。這個時候subselect效率比較高,不管in里含有多少數據,在查詢Order是,只會發出一條sql語句。把<set>集合中batch-size設置為一個比較合適的數值時也相當於fetch為subselect,你可以根據項目的因素來選擇發出sql語句的次數。


HQL總是忽略映射文件中設置的預先抓取策略,即在HQL中使用預先抓取時,必須顯示指明fetch關鍵字。然而不同的是,QBC則不會忽略映射文件中的預先抓取策略。

在實踐開發項目過程中,不僅需要根據實際情況選擇合適的抓取策略,而且需要通過不斷的測試來驗證這個策略是不是最有效率的。




三、緩存管理
1.緩存概述
  緩存(cache)在java應用程序中是一組內存中的集合實例。它保存着永久性存儲源(如硬盤上的文件或者數據庫)中數據的備份,它的讀寫速度比讀寫硬盤的速度快。應用程序在運行時直接讀寫緩存中的數據,只在某些特定時刻安裝緩存中的數據來同伴更新數據存儲源。如果緩存中存放的數據量非常大,也會用硬盤作為緩存的物理介質。
  緩存的作用就是降低應用程序直接讀寫永久性數據存儲源的頻率,從而增強應用的運行性能。
緩存的實現不僅需要作為物理介質的硬件(內存),同時還需要用於管理緩存並發訪問和過期等策略的軟件。
2 緩存范圍分類
緩存的范圍決定了緩存的生命周期及其可以被誰訪問。緩存的范圍分為以下三類:
1)事務范圍:緩存只能被當前事務訪問。緩存的生命周期依賴於事務的生命周期,當事務結束時,緩存也就結束生命周期。在此范圍下,緩存的介質是內存。
2)進程范圍:緩存被進程內的所有事務共享。這些事務有可能是並發訪問緩存,因此必須對緩存采取必要的事務隔離機制。
3)集群范圍:在集群環境中,緩存被一個機器或者多個機器的進程共享。緩存的數據被復制到集群環境中的每個進程節點,進程間通過遠程通信來保證緩存中的數據一致性。對於大多數應用來說,應該慎用集群范圍的緩存,因為訪問的速度並不一定比直接訪問數據庫數據的速度快很多。
3. 緩存的並發訪問策略
在進程范圍或集群范圍的緩存,會出現並發問題。因此可以設置4中類型的並發訪問策略,每一種策略對應一種事務隔離級別。事務的隔離級別越高,並發性能就越低。
1)事務型(Transactional)策略
2)讀寫型(Read-Write)策略
3)非嚴格讀寫型(Nonstrict-read-write)策略
4)只讀型(Read-only)策略


4.Hibernate中的緩存
Hibernate中提供兩級緩存,第一級別的緩存是Session級別的緩存,它是屬於事務范圍的緩存。
第二級別的緩存是SessionFactory級別的緩存,它是屬於進程范圍范圍的緩存,這一級別的緩存可以進行配置和更改,並且可以動態加載和卸載。
Hibernate還為查詢結果提供一個查詢緩存,它依賴於二級緩存。




5.一級緩存的管理
Session級別的緩存由hibernate自動管理。當應用程序調用Session的CRUD方法及調用查詢接口的list(),iterate()等方法時,如果在Session緩存中還不存在相應的對象,Hibernate就會把改對象加入到Session緩存中。如果在Session緩存中已經存在這個對象,就不需要再去數據庫加載而是直接使用緩存中的這個對象,可以減少訪問數據庫的頻率,提高程序的運行效率。當Hibernate清理緩存時(默認是提交事務的時候),Hibernate會根據緩存中對象的狀態來同步數據庫中的數據狀態,在關閉Session時,會清空Session緩存中的所有對象。



一級緩存不能控制緩存的數量,所以要注意大批量操作數據時可能造成內存溢出;可以用
evict(Object obj):從緩存中清除指定的持久化對象。

clear():清空緩存中所有持久化對象。 

flush(): 進行清理緩存(此時緩存中的數據並不丟失)的操作,讓緩存和數據庫同步 執行一些列sql語句,但不提交事務。

commit():先調用flush() 方法,然后提交事務. 則意味着提交事務意味着對數據庫操作永久保存下來。



可以寫一個for循環,Session可以批量插入上萬條數據。如下面的代碼:
            For(int i=0;i<10000;i++){
  Session.save(object);
}
這樣寫的代碼會產生一個什么問題?會使一萬個對象的緩存全部存在於內存中,這樣做加大了內存的壓力。所以應該定期清理session的緩存。
當做批量插入或批量更新時,必須通過經常調用Session的flush()以及稍后調用clear()來控制一級緩存的大小,這樣內存才能保證足夠的空間。



for(int i=1;i<=50;i++){
Department dept = new Department();
dept.setName("軟件"+i);
session.save(dept);
for(int j=1;j<=100;j++){
Employee emp = new Employee();
emp.setName("張三"+j);
emp.setSalary(j);
    emp.setDept(dept
    session.save(emp);
    if(j%50==0){
    session.flush();
    session.clear();
    }
}
}
6.二級緩存的管理
二級緩存是SessionFactory級別的緩存,它的使用過程如下:
1)執行條件查詢的時候,發出“select * from table_name where …”這樣的SQL語句查詢數據庫,一次獲得所有的數據對象。
2)把獲得的所有數據對象根據ID放入到二級緩存中。
3)當Hibernate根據ID訪問數據對象時,首先從Session緩存中查;查不到,如果配置了二級緩存,那么從二級緩存中查;還沒查到,再查詢數據庫,把結果按照ID放入到二級緩存。
4)刪除、更新和增加數據的時候,同時會更新到二級緩存中。
Hibernate的二級緩存策略是針對ID查詢的緩存策略,對於調節查詢則毫無作用。為此,hibernate提高了單獨針對條件查詢的查詢緩存。


適合存放到二級緩存中的數據有:
1)很少被修改的數據。
2)不是很重要的數據,允許出現偶爾的並發的數據。
3)很多系統模塊都要用到
4)不是私有的數據,是共享的

什么樣的數據不適合放在二級緩存中???
財務數據  安全性的數據 也就是不想讓別人看到的數據和特別重要的數據
1、二級緩存的管理
二級緩存是SessionFactory級別的緩存,它的使用過程如下:
1)執行條件查詢的時候,發出“select * from table_name where …”這樣的SQL語句查詢數據庫,一次獲得所有的數據對象。
2)把獲得的所有數據對象根據ID放入到二級緩存中。
3)當Hibernate根據ID訪問數據對象時,首先從Session緩存中查;查不到,如果配置了二級緩存,那么從二級緩存中查;還沒查到,再查詢數據庫,把結果按照ID放入到二級緩存。
4)刪除、更新和增加數據的時候,同時會更新到二級緩存中。
Hibernate的二級緩存策略是針對ID查詢的緩存策略,對於調節查詢則毫無作用。為此,hibernate提高了單獨針對條件查詢的查詢緩存。

適合存放到二級緩存中的數據有:
1)很少被修改的數據。
2)不是很重要的數據,允許出現偶爾的並發的數據。
3)很多系統模塊都要用到
4)不是私有的數據,是共享的

什么樣的數據不適合放在二級緩存中???
財務數據  安全性的數據 也就是不想讓別人看到的數據和特別重要的數據

2、查詢緩存
如果在實際使用中對於某個條件查詢語句經常使用相同的條件值進行查詢,就可以啟用查詢緩存。
Hibernate的查詢緩存策略的過程如下。
1)在第一次執行條件查詢時,Hibernate首先根據這些條件值組成一個Query key,Query key包括條件查詢的請求信息。
2)在后續的過程中用相同的條件值執行這個查詢是,Hibernate就先根據這個Query key到查詢緩存中查找對應的結果列表,如果存在,返回。如果不存在,查數據庫,再把結果列表根據Query 可以存放在查詢緩存中。
所以,只有當經常使用相同的參數值進行相同的條件查詢時,才能從查詢緩存策略中得到好處。


使用查詢緩存的步驟
1.配置查詢緩存
Hibernate提供了3種與查詢相關的緩存區域。
默認的查詢緩存區域:org.hibernate.cache.StandardQueryCache
用戶自定義的查詢緩存區域
時間戳緩存區域:org.hibernate.cache.UpdateTimestampCache

在ehcache的配置文件ehcache.xml中設置查詢緩存區域的屬性。

<ehcache>
<!-- 設置默認的查詢緩存區域的屬性 -->
    <cache name="org.hibernate.cache.StandardQueryCache" 
    maxElementsInMemory="50"
    eternal="false"
    overflowToDisk="true"
    timeToIdleSeconds="3600" 
    timeToLiveSeconds="7200" 
    />
  
<!-- 設置時間戳緩存區域的屬性 -->
<cache name="org.hibernate.cache.UpdateTimestampsCache" 
  maxElementsInMemory="500"
  eternal="true" 
  overflowToDisk="true"
  /> 
    
    <!-- 設置自定義命名查詢緩存區域的屬性 -->
    <cache name="myCacheRegion"
        maxElementsInMemory="1000"
        eternal="false"
        overflowToDisk="true"
        timeToIdleSeconds="3600"
        timeToLiveSeconds="7200"
        />
</ehcache>
3、

打開查詢緩存
在hibernate的全局配置文件中添加如下配置來啟動查詢緩存。
<!--啟用查詢緩存 -->
<property name="cache.use_query_cache">true</property>

在應用程序代碼中使用查詢緩存
雖然按以上步驟設置好查詢緩存,但hibernate在執行條件查詢時默認是忽略查詢緩存的。如果希望啟用查詢緩存,應該調用Query接口的setCacheeable(true)方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM