2014-02-17 71 views
3

我有一個spring/springwebflow(應用程序A)應用程序,它使用Hibernate二級緩存並查詢緩存。應用程序只能從數據庫中讀取,而不能添加/更新/刪除語句。休眠緩存註釋區域屬性不起作用

查詢被緩存到一個名爲「daoCache」的區域。我已經在namedQuerys設置此通過註解

我有實體從休眠@Cache的註釋與「daoCache」屬性區域集合太

@Entity 
@Table(name = "GROUP_OPERATOR") 
@SequenceGenerator(name = "SEQ_GROUP_OPERATOR", sequenceName = "SEQ_GROUP_OPERATOR") 
@NamedQueries({ 
@NamedQuery(name = "findGroupOperatorByMaster", query = "FROM GroupOperator groupOperator WHERE groupOperator.segment IS NULL and groupOperator.pointsMaster.pointMaster like ?", 
    hints={@QueryHint(name="org.hibernate.cacheRegion", value="daoCache"), 
     @QueryHint(name="org.hibernate.cacheable", value="true")}) 
}) 
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "daoCache") 
public class GroupOperator implements Serializable{ 
.... 

除此之外我還有一個應用程序(應用B )連接到相同的數據庫並通過相同的數據庫執行添加/更新/刪除

因此,我們設置了20分鐘的限制來刷新應用程序A上的緩存區域。因此,在應用程序B中進行的更改是反映在20分鐘後的應用程序A.

因此,讓我們假設應用程序A執行查詢「select * from group_operator where point_master = 1000」。該查詢將被保存在帶有結果標識符的緩存中,比如groupOperator#1。因此,該查詢執行的任何其他時間(20分鐘之間)都將從緩存中返回標識符#1。並且該實體也將被保存在相同的緩存區域中。

問題是,當應用程序乙刪除的記錄,並創建一個新的(具有不同的ID), *與查詢*用作參數

所以相同的值,則間20分鐘,應用程序A將返回實體groupOperator#1,並且沒有選擇語句顯示在應用程序日誌中。這是預期的行爲。

20分鐘後,緩存將被重置。

現在,App A嘗試執行相同的查詢(「select * from group_operator where point_master = 1000」)。它將對數據庫執行查詢,因爲緩存在20分鐘後被清除。所以結果應該是在App B中創建的新ID。但是,我得到一個ObjectNotFoundException不存在具有給定標識符的行:groupOperator#1

即使select語句顯示在日誌中,但仍然返回舊的實體標識符。當然,ID 1不再存在於數據庫中。

這很奇怪,因爲我認爲,清除緩存將再次執行查詢

如果兩個實體和查詢中爲什麼還在尋找舊實體相同的區域被緩存?

經過幾天的調查,使用堆轉儲,我發現該實體沒有緩存在@Cache註釋中指定的「daoCache」上。但被緩存在名爲「xxx.xxx.xxx.xx.groupOperator」的新區域中。似乎該註釋的屬性不起作用。

我有幾個問題

1,爲什麼在緩存標註的區域屬性不工作?即使我設置「daoCache」是beign保存在另一個?這是來自休眠的錯誤?

2 - 查詢緩存的工作原理是什麼?我的意思是,如果再次執行查詢,它應該返回新的標識符,然後用這個新標識符hibernate應該執行加載方法並解決問題。但仍嘗試從舊的標識符加載。

這是我的實際ehcache.xml中配置

<cache name="daoCache" maxElementsInMemory="30000" eternal="false" 
     timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"> 
    </cache> 

    <cache name="rwCache" maxElementsInMemory="30000" eternal="false" 
     timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"> 
    </cache> 

    <cache name="xxx.xxxx.xxx.xxxx.groupOperator" maxElementsInMemory="30000"  eternal="false" 
     timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"> 
    </cache> 

這是我的緩存提供

<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> 

使用以下版本冬眠

<dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-ehcache</artifactId> 
    <version>3.3.2.GA</version> 
</dependency> 


<dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-core</artifactId> 
    <version>3.3.2.GA</version> 
</dependency> 

<dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-commons-annotations 
    </artifactId> 
    <version>3.2.0.Final</version> 
</dependency> 

感謝您的時間

UPDATE:

我沒有指定任何hibernate.cache.region.factory_class。所以我想是使用默認的。不知道哪一個是默認的

查詢在類中有註釋queryhint。即使我從表中刪除所有記錄,仍然返回舊記錄而不執行任何對數據庫的選擇,這是工作正常的,正在緩存查詢。所以查詢緩存工作得很好。

的問題是:...

刪除表中的所有記錄,並驗證查詢緩存後的作品我添加了一個新的記錄到同一個表具有相同值之前,但不同的主ID

清除緩存後(20分鐘後自動),再次通過數據庫執行查詢(這是可以的,我可以看到日誌中的select語句)。找到表中的新記錄,並且應該返回一個標識符,然後將整個實體加載到內存中。奇怪的是,當它試圖讓整個實體進入內存時,正在使用舊標識符來查找該實體,而不是之前添加的新實體。就好像如果查詢,甚至是再次通過數據庫執行,仍然返回舊的標識符。但我再說一遍,查詢緩存在其他情況下正常工作,這是肯定的。

所以,我不明白查詢緩存和實體緩存如何協同工作。就像查詢緩存和實體緩存之間的問題一樣。我說這是因爲我通過在ehcache.xml中添加該實體的區域配置來解決此問題。

<cache 
    name="com.citi.latam.business.services.dao.model.db.campaign.groupOperator" 
    maxElementsInMemory="30000" eternal="false" timeToIdleSeconds="120" 
    timeToLiveSeconds="120" overflowToDisk="true"> 
</cache> 

回答

0

哪個hibernate.cache.region.factory_class您使用,EhCacheRegionFactorySingletonEhCacheRegionFactory?這可能與第1點有關。

對於第2點,它的工作原理與您所提到的相同,但查詢需要單獨標記爲可緩存。在標準API中,這是通過setCacheable(true)和JPQL中的查詢提示完成的。

對於兩個緩存不同步的問題,查詢緩存不一定會與實體所在的緩存區域同時被逐出。

查詢默認緩存在默認緩存區域中,但可以選擇使用API​​ criteria.setCacheRegion()緩存查詢的區域。

通過將有問題的查詢放置在與實體相同的緩存區域中,這將確保查詢緩存的結果和緩存的實體同時被驅逐。

+0

我回答了你的問題。請看一下。 謝謝 – user3320292

+0

我編輯了同時驅逐高速緩存的答案,但對於選中命中數據庫並仍然返回只有在未提交刪除/插入事務時纔有可能的古代值,或者隔離級別爲REPEATABLE_READ/SERIALIZABLE由定期查詢的服務保存大量時間(20分鐘) –

+0

謝謝!事實上,我正在執行主應用程序之外的刪除事務(從應用程序B)。因此,主應用程序不知道從應用程序B所做的更改。這是一種情況:應用程序A讀取,應用程序B刪除記錄,應用程序B創建新記錄,其值與刪除的值相同,但ID不同,應用程序A再次讀取,會引發NotEntityFoundException。我的猜測是,應用程序A中的休眠會話沒有意識到表中的變化,因此休眠意味着根本沒有任何變化,仍然在尋找古代標識符。可能與UpdateTimestampsCache有關,請看看 – user3320292

2

固定!

我終於解決了。 首先,我所做的就是讓所有從休眠和ehcache的

在日誌中搜索的調試,我發現我的數據庫沒有正確配置型號

對象GroupOperator有一個名爲Calendar屬性。 問題在於對象Calendar還具有Cache註釋的GroupOperator列表。因此,每當Hibernate獲得新的GroupOperator時也想要刷新像日曆這樣的子實體,但日曆有他自己的GroupOperator列表,並且似乎子實體不在同一緩存區域中或以不同方式工作。

問題解決! 感謝您的時間@jhadesdev,有助於繼續調查此事。

Regards,