程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> iBatis使用OSCache作緩存與java.io.FileNotFoundException異常

iBatis使用OSCache作緩存與java.io.FileNotFoundException異常

編輯:關於JAVA

這種異常只會發生在 Windows 平台上,可以說是 iBatis 的一個 Bug,大約 iBatis 的開發測試人員都比較熱衷於 Unix 族系的平台,而忽略了 Windows 下的兼容性測試。

出現 FileNotFoundException 異常的情形是這樣的:Windows7 + iBatis2.3.0.677 + OSCache2.4.1。

iBatis 中配置使用 OSCache 緩存,在映射文件 Product.xml 中有如下聲明片斷:

1.<cacheModel type="OSCACHE" id="productCache">
2.<flushOnExecute statement="Product.insert"/>
3.<flushOnExecute statement="Product.delete"/>
4.</cacheModel>
5.
6.<select id="getById" cacheModel="productCache" parameterClass="int" resultClass="Product">
7.select id, name,description as desc1  from test_product where id = #value#
8.</select>

OSCache 的 oscache.properties 中指明用磁盤文件緩存數據,注意以下幾個配置:

01.#不緩存到內存
02.cache.memory=false
03.
04.#緩存持久化實現類,磁盤持久化監聽器
05.cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener
06.
07.#緩存磁盤路徑,以後注意觀察該目錄生成的內容
08.cache.path=e:\\OSCache\\cache
09.
10.#選擇一種緩存算法,還可選 FIFOCache、UnlimitedCache
11.cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache

Java 測試代碼:

1.public static void main(String[] args) throws Exception{
2.Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
3.SqlMapClient sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);
4.System.out.println(sqlMapClient.queryForObject("Product.getById",5));
5.
6.//後面同樣的查詢將不再查詢數據庫,而是直接使用前面執行放在緩存中的內容
7.System.out.println(sqlMapClient.queryForObject("Product.getById",5));
8.}

執行上面那段代碼,你將會得到異常:

2009-8-19 10:45:52 com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache persistStore
嚴重: [oscache] Exception persisting 1991729862|2162672802|5|Product.getById|62996507|         select id, name,description as desc1  from test_product where id = ?     |executeQueryForObject
com.opensymphony.oscache.base.persistence.CachePersistenceException: Unable to write 'e:\OSCache\cache\application\1991729862|2162672802|5|Product_31getById|62996507|_49_50_51_52_53_54_55_56_57select_64id,_68name,description_85as_88desc1_94_95from_100test_105product_113where_119id_122=_124_125_126_127_128_129_130|executeQueryForObject.cache' in the cache.Exception: java.io.FileNotFoundException, Message: e:\OSCache\cache\application\1991729862|2162672802|5|Product_31getById|62996507|_49_50_51_52_53_54_55_56_57select_64id,_68name,description_85as_88desc1_94_95from_100test_105product_113where_119id_122=_124_125_126_127_128_129_130|executeQueryForObject.cache (文件名、目錄名或卷標語法不正確。)
at com.opensymphony.oscache.plugins.diskpersistence.AbstractDiskPersistenceListener.store(AbstractDiskPersistenceListener.java:376)
at com.opensymphony.oscache.plugins.diskpersistence.AbstractDiskPersistenceListener.store(AbstractDiskPersistenceListener.java:238)
at com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache.persistStore(AbstractConcurrentReadCache.java:1113)
at com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache.put(AbstractConcurrentReadCache.java:1623)
at com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache.put(AbstractConcurrentReadCache.java:864)
at com.opensymphony.oscache.base.Cache.putInCache(Cache.java:637)
at com.opensymphony.oscache.base.Cache.putInCache(Cache.java:614)
at com.opensymphony.oscache.general.GeneralCacheAdministrator.putInCache(GeneralCacheAdministrator.java:270)
at com.ibatis.sqlmap.engine.cache.oscache.OSCacheController.putObject(OSCacheController.java:70)
at com.ibatis.sqlmap.engine.cache.CacheModel.putObject(CacheModel.java:318)
at com.ibatis.sqlmap.engine.mapping.statement.CachingStatement.executeQueryForObject(CachingStatement.java:80)
at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:566)
at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:541)
at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForObject(SqlMapSessionImpl.java:106)
at com.ibatis.sqlmap.engine.impl.SqlMapClientImpl.queryForObject(SqlMapClientImpl.java:83)
at com.unmi.TestClient.main(TestClient.java:25)

無法寫某個文件,文件找不到,一開始我也以為是 OSCache 那方出了什麼差錯,所以 Google 的關鍵詞都偏重於 OSCache FileNotFoundExcption。後來結合 iBatis 才艱難的 Google 到了這個:http://osdir.com/ml/java.ibatisdb.devel/2004-06/msg00079.html,也有同問的:http://forums.opensymphony.com/thread.jspa?messageID=673372&。

看了前面那個貼子,拍了下腦袋,其實早該醒悟的,不就是說的:'e:\OSCache\cache\application\1991729862|2162672802|5|Product_31getById|62996507|_49_50_51_52_53_54_55_56_57select_64id,_68name,description_85as_88desc1_94_95from_100test_105product_113where_119id_122=_124_125_126_127_128_129_130 文件無法寫嗎。去看看 e:\OSCache\cache\application 目錄中,沒有這裡提到的文件。如果你試圖去手工建立這麼一個文件,文件名有多長且不說,但 Windows 肯定要提示你:文件名不能包含下面任何字符: \ / : * ? " < > | 。對啦,問題就出在 iBatis 為 OSCache 生成的文件名上。

開源的東西對我們使用者有什麼好處,就是能完全窺探到源代碼呀。我的習慣是開源的組件,總會下載到相應的源代碼擺在它的身邊。於是 Debug/Step Into 吧,發現前面那個長長的文件名其實就是 OSCache 用來緩存 iBatis 查詢得到結果(注:iBatis 被緩存的粒度沒 Hibernate 的緩存細,不是以實體對象為單位,而是以查詢結果為單位,所以會出現第一次查詢的 id=2 or id=3 的結果被緩存了,第二次查詢 id=2 時還是從數據庫中取得情況) 的 Key,這個 Key 是基於查詢語句(含查詢條件) 生成的。實現類是 iBatis 的 com.ibatis.sqlmap.engine.cache.CacheKey,它的 toString() 方法返回值就是文件名:

1.public String toString() {
2.StringBuffer returnValue = new StringBuffer().append(hashcode).append('|').append(checksum);
3.for (int i=0; i < paramList.size(); i++) {
4.returnValue.append('|').append(paramList.get(i));
5.}
6.
7.return returnValue.toString();
8.}

上面代碼生成的文件名就可能包含豎線符號("|"),這在 Windows 平台是不允許的,在 Linux 下是不受此約束的。為了能讓 iBatis 在使用文件緩存時保持平台獨立性,我們的 Fix 辦法可使在該 toString() 方法最後返回時把 | 換成都能夠接受的符號,比如 -,即把 return 語句改為:

return returnValue.toString().replace('|', '-');

可以把修正編譯好的 CacheKey.class 替換掉 iBatis jar 包中的相應位置的 CacheKey.class,或者若是 Web 應用,可直接放在 classes 目錄中正確位置上,因為 classes 中散裝的類文件要優先於 jar 包中的類得到加載。

問題需解決了,但還是得注意幾點:

1.即使是使用其它緩存實現,也都是由這個 CacheKey 來生成 Key 的,所以若有可能使用其它緩存組件,緩存文件時同樣有可能出現類似的問題。

2.如果 CacheKey 在結合 SQL 和 參數生成的文件名過長可能也會是個問題,受限於不同的文件系統。設置的緩存目錄不要過深,適時完全自定 toString() 方法。

3.還有,關於是從緩存中取數據還是從數據庫取得問題,這個 CacheKey 的 equals() 方法值得細究,這是我的後話了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved