hibernate3與ehcache-2.8.3配合使用,在多個SessionFactory實例的情況下出現“Another unnamed CacheManager already exists in the same VM”問題


不想看分析過程的,直接拉到最下面看第4部分

1. 問題背景

由於某些原因,項目中使用了hibernate3與ehcache-2.8.3在配合使用,又由於某些原因,需要使用多個SessionFactory實例。在這個背景下,啟動項目報錯,具體異常如下:

Caused by: net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: DefaultConfigurationSource [ ehcache.xml or ehcache-failsafe.xml ]
    at net.sf.ehcache.CacheManager.assertNoCacheManagerExistsWithSameName(CacheManager.java:591)
    at net.sf.ehcache.CacheManager.init(CacheManager.java:385)
    at net.sf.ehcache.CacheManager.<init>(CacheManager.java:369)
    at org.hibernate.cache.EhCacheProvider.start(EhCacheProvider.java:124)
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:183)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1294)
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:860)

大意就是同一個vm中有兩個未命名的CacheManager存。具體代碼細節可以根據上面的報錯堆棧很容易發現:

private void assertNoCacheManagerExistsWithSameName(Configuration configuration) {
        synchronized (CacheManager.class) {
            final String name;
            final boolean isNamed;
            if (configuration.getName() != null) {
                name = configuration.getName();
                isNamed = true;
            } else {
                name = DEFAULT_NAME;
                isNamed = false;
            }
            CacheManager cacheManager = CACHE_MANAGERS_MAP.get(name);
            if (cacheManager == null) {
                CACHE_MANAGERS_MAP.put(name, this);
                CACHE_MANAGERS_REVERSE_MAP.put(this, name);
            } else {
                ConfigurationSource configurationSource = cacheManager.getConfiguration().getConfigurationSource();
                final String msg = "Another "
                        + (isNamed ? "CacheManager with same name '" + name + "'" : "unnamed CacheManager")
                        + " already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:\n"
                        + "1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name"
                        + " or create one if necessary\n"
                        + "2. Shutdown the earlier cacheManager before creating new one with same name.\n"
                        + "The source of the existing CacheManager is: "
                        + (configurationSource == null ? "[Programmatically configured]" : configurationSource);
                throw new CacheException(msg);
            }
        }
    }

2. 通過hibernate的配置項”hibernate.cache.provider_configuration_file_resource_path”單獨指定SessionFactory每個實例的ehcache配置文件

hibernate與ehcahe對接的地方能發現些什么?

觀察堆棧:

at org.hibernate.cache.EhCacheProvider.start(EhCacheProvider.java:124)
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:183)

發先如下代碼:

String configurationResourceName = null;
            if (properties != null) {
                configurationResourceName = (String) properties.get( Environment.CACHE_PROVIDER_CONFIG );
            }
            if ( StringHelper.isEmpty( configurationResourceName ) ) {
                manager = new CacheManager();
            } else {
                URL url = loadResource(configurationResourceName);
                manager = new CacheManager(url);
            }

這里有重要的一個發現:

初始化SessionFactory實例時啟動EhCacheProvider

啟動EhCacheProvider時創建CacheManager實例,

創建CacheManager實例時可以通過hibernate的配置項hibernate.cache.provider_configuration_file_resource_path來決定CacheManager的配置

hibernate.cache.provider_configuration_file_resource_path這個配置項的值是什么形式?

分析org.hibernate.util.ConfigHelper.locateConfig(String)代碼:

public static final URL locateConfig(final String path) {
        try {
            return new URL(path);
        }
        catch(MalformedURLException e) {
            return findAsResource(path);
        }
    }

不難發現:

1).優先用標准的url形式查找。

2).失敗后用classpath的方式查找。

3. 如何在ehcache的xml配置文件中指定CacheManager實例的名字

分析1中的assertNoCacheManagerExistsWithSameName代碼發現cacheManager的name是從net.sf.ehcache.config.Configuration獲取的。

net.sf.ehcache.config.Configuration實例又是依賴SAX解析配置文件xml實現的。

SAX解析需要配合handler使用,這里是net.sf.ehcache.config.BeanHandler這個類。

調試BeanHandler后發現,Configuration實例的cacheManagerName就是配置文件中ehcache xml配置文件中ehcache標簽的name屬性的值。

<ehcache name="jbpmEhcacheManager">

4. 總結解決辦法:

1). 在classpath下放置多個ehcachexxx.xml配置文件(有多少個SessionFactory實例就放置多少個,文件名不同),每個配置文件中ehcache標簽的name屬性值要唯一。

2). 在每個SessionFactory實例裝配時(如果編程式的就用api指定),指定hibernate.cache.provider_configuration_file_resource_path的值為相應的ehcache配置名

<prop key="hibernate.cache.provider_class">
                    org.hibernate.cache.EhCacheProvider
                </prop>
                
                <!-- hibernate3 配合ehcache2.5之后的版本使用,對於多個SessionFactory實例,需要為每個SessionFactory實例指定獨立的ehcache的配置文件 -->
                <!-- hibernate3對這一配置項的路徑尋找方式為:1.優先用標准的url形式查找。2.失敗后用classpath的方式查找。此處使用2方式。 -->
                <prop key="hibernate.cache.provider_configuration_file_resource_path">ehcache4common.xml</prop>

--EOF--


免責聲明!

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



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