欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从应用角度看Hibernate源码(二):Hibernate缓存 HibernateCache.netJBossApache 

程序员文章站 2024-03-19 17:17:22
...

        Hibernate3.0以前,Hibernate的性能不咋地。运行一段程序慢的要死,而且还不够稳定。但运行Hibernate3后发现性能改良了不少。其中一个主要的原因也是缓存的优化处理。Hibernate缓存的处理放在了源码的org.hibernate.cache目录下。下面我们就谈一谈这个目录:
(1)Hibernate支持多少种缓存。在一般的程序员眼里,恐怕只知道,Hibernate只支持EhCache。但打开
这个目录,你会发现不是那么回事。Hibernate支持大部分的缓存技术。如还有OSCache,TreeCache等等。如果有时间你可以点着每个类看看。只要符合下面的样子就是一种缓存。

java 代码
  1. public class XXXCache implements Cache {   
  2.   
  3. 。。。。。。   
  4. }  

这些缓存有些是Hibernate特有的,有些是第3方的。不过从另外的角度来说,缓存的策略基本都是一样的。打开Ehcache和Oscache比较一下就会发现。内容实现基本上一致的。因为基本都一致,所以当时开发Hibernate人员,认为加一是加,加十也是加。所以就都给加上了。这个只是我估计的。

(2)Cache的实现策略。以EhCache为例。实现第三方的缓存需要干两件事情。
一件是实现Cache接口,代码如下所示:

  1. //$Id: EhCache.java 10716 2006-11-03 19:05:11Z max.andersen@jboss.com $   
  2. /**  
  3.  *  Copyright 2003-2006 Greg Luck, Jboss Inc  
  4.  *  
  5.  *  Licensed under the Apache License, Version 2.0 (the "License");  
  6.  *  you may not use this file except in compliance with the License.  
  7.  *  You may obtain a copy of the License at  
  8.  *  
  9.  *      http://www.apache.org/licenses/LICENSE-2.0  
  10.  *  
  11.  *  Unless required by applicable law or agreed to in writing, software  
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,  
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  14.  *  See the License for the specific language governing permissions and  
  15.  *  limitations under the License.  
  16.  */  
  17. package org.hibernate.cache;   
  18.   
  19. import java.io.IOException;   
  20. import java.util.HashMap;   
  21. import java.util.Iterator;   
  22. import java.util.Map;   
  23.   
  24. import net.sf.ehcache.CacheManager;   
  25. import net.sf.ehcache.Element;   
  26. import org.apache.commons.logging.Log;   
  27. import org.apache.commons.logging.LogFactory;   
  28.   
  29. /**  
  30.  * EHCache plugin for Hibernate  
  31.  * 

     

     
  32.  * EHCache uses a {@link net.sf.ehcache.store.MemoryStore} and a  
  33.  * {@link net.sf.ehcache.store.DiskStore}.  
  34.  * The {@link net.sf.ehcache.store.DiskStore} requires that both keys and values be {@link java.io.Serializable}.  
  35.  * However the MemoryStore does not and in ehcache-1.2 nonSerializable Objects are permitted. They are discarded  
  36.  * if an attempt it made to overflow them to Disk or to replicate them to remote cache peers.  
  37.  *  
  38.  * @author Greg Luck  
  39.  * @author Emmanuel Bernard  
  40.  */  
  41. public class EhCache implements Cache {   
  42.     private static final Log log = LogFactory.getLog( EhCache.class );   
  43.   
  44.     private static final int SIXTY_THOUSAND_MS = 60000;   
  45.   
  46.     private net.sf.ehcache.Cache cache;   
  47.   
  48.     /**  
  49.      * Creates a new Hibernate pluggable cache based on a cache name.  
  50.      * 

     

     
  51.      *  
  52.      * @param cache The underlying EhCache instance to use.  
  53.      */  
  54.     public EhCache(net.sf.ehcache.Cache cache) {   
  55.         this.cache = cache;   
  56.     }   
  57.   
  58.     /**  
  59.      * Gets a value of an element which matches the given key.  
  60.      *  
  61.      * @param key the key of the element to return.  
  62.      * @return The value placed into the cache with an earlier put, or null if not found or expired  
  63.      * @throws CacheException  
  64.      */  
  65.     public Object get(Object key) throws CacheException {   
  66.         try {   
  67.             if ( log.isDebugEnabled() ) {   
  68.                 log.debug( "key: " + key );   
  69.             }   
  70.             if ( key == null ) {   
  71.                 return null;   
  72.             }   
  73.             else {   
  74.                 Element element = cache.get( key );   
  75.                 if ( element == null ) {   
  76.                     if ( log.isDebugEnabled() ) {   
  77.                         log.debug( "Element for " + key + " is null" );   
  78.                     }   
  79.                     return null;   
  80.                 }   
  81.                 else {   
  82.                     return element.getObjectValue();   
  83.                 }   
  84.             }   
  85.         }   
  86.         catch (net.sf.ehcache.CacheException e) {   
  87.             throw new CacheException( e );   
  88.         }   
  89.     }   
  90.   
  91.     public Object read(Object key) throws CacheException {   
  92.         return get( key );   
  93.     }   
  94.   
  95.   
  96.     /**  
  97.      * Puts an object into the cache.  
  98.      *  
  99.      * @param key   a key  
  100.      * @param value a value  
  101.      * @throws CacheException if the {@link CacheManager}  
  102.      *                        is shutdown or another {@link Exception} occurs.  
  103.      */  
  104.     public void update(Object key, Object value) throws CacheException {   
  105.         put( key, value );   
  106.     }   
  107.   
  108.     /**  
  109.      * Puts an object into the cache.  
  110.      *  
  111.      * @param key   a key  
  112.      * @param value a value  
  113.      * @throws CacheException if the {@link CacheManager}  
  114.      *                        is shutdown or another {@link Exception} occurs.  
  115.      */  
  116.     public void put(Object key, Object value) throws CacheException {   
  117.         try {   
  118.             Element element = new Element( key, value );   
  119.             cache.put( element );   
  120.         }   
  121.         catch (IllegalArgumentException e) {   
  122.             throw new CacheException( e );   
  123.         }   
  124.         catch (IllegalStateException e) {   
  125.             throw new CacheException( e );   
  126.         }   
  127.         catch (net.sf.ehcache.CacheException e) {   
  128.             throw new CacheException( e );   
  129.         }   
  130.   
  131.     }   
  132.   
  133.     /**  
  134.      * Removes the element which matches the key.  
  135.      * 

     

     
  136.      * If no element matches, nothing is removed and no Exception is thrown.  
  137.      *  
  138.      * @param key the key of the element to remove  
  139.      * @throws CacheException  
  140.      */  
  141.     public void remove(Object key) throws CacheException {   
  142.         try {   
  143.             cache.remove( key );   
  144.         }   
  145.         catch (ClassCastException e) {   
  146.             throw new CacheException( e );   
  147.         }   
  148.         catch (IllegalStateException e) {   
  149.             throw new CacheException( e );   
  150.         }   
  151.         catch (net.sf.ehcache.CacheException e) {   
  152.             throw new CacheException( e );   
  153.         }   
  154.     }   
  155.   
  156.     /**  
  157.      * Remove all elements in the cache, but leave the cache  
  158.      * in a useable state.  
  159.      *  
  160.      * @throws CacheException  
  161.      */  
  162.     public void clear() throws CacheException {   
  163.         try {   
  164.             cache.removeAll();   
  165.         }   
  166.         catch (IllegalStateException e) {   
  167.             throw new CacheException( e );   
  168.         }   
  169.         catch (net.sf.ehcache.CacheException e) {   
  170.             throw new CacheException( e );   
  171.         }   
  172.     }   
  173.   
  174.     /**  
  175.      * Remove the cache and make it unuseable.  
  176.      *  
  177.      * @throws CacheException  
  178.      */  
  179.     public void destroy() throws CacheException {   
  180.         try {   
  181.             cache.getCacheManager().removeCache( cache.getName() );   
  182.         }   
  183.         catch (IllegalStateException e) {   
  184.             throw new CacheException( e );   
  185.         }   
  186.         catch (net.sf.ehcache.CacheException e) {   
  187.             throw new CacheException( e );   
  188.         }   
  189.     }   
  190.   
  191.     /**  
  192.      * Calls to this method should perform there own synchronization.  
  193.      * It is provided for distributed caches. Because EHCache is not distributed  
  194.      * this method does nothing.  
  195.      */  
  196.     public void lock(Object key) throws CacheException {   
  197.     }   
  198.   
  199.     /**  
  200.      * Calls to this method should perform there own synchronization.  
  201.      * It is provided for distributed caches. Because EHCache is not distributed  
  202.      * this method does nothing.  
  203.      */  
  204.     public void unlock(Object key) throws CacheException {   
  205.     }   
  206.   
  207.     /**  
  208.      * Gets the next timestamp;  
  209.      */  
  210.     public long nextTimestamp() {   
  211.         return Timestamper.next();   
  212.     }   
  213.   
  214.     /**  
  215.      * Returns the lock timeout for this cache.  
  216.      */  
  217.     public int getTimeout() {   
  218.         // 60 second lock timeout   
  219.         return Timestamper.ONE_MS * SIXTY_THOUSAND_MS;   
  220.     }   
  221.   
  222.     public String getRegionName() {   
  223.         return cache.getName();   
  224.     }   
  225.   
  226.     /**  
  227.      * Warning: This method can be very expensive to run. Allow approximately 1 second  
  228.      * per 1MB of entries. Running this method could create liveness problems  
  229.      * because the object lock is held for a long period  
  230.      * 

     

     
  231.      *  
  232.      * @return the approximate size of memory ehcache is using for the MemoryStore for this cache  
  233.      */  
  234.     public long getSizeInMemory() {   
  235.         try {   
  236.             return cache.calculateInMemorySize();   
  237.         }   
  238.         catch (Throwable t) {   
  239.             return -1;   
  240.         }   
  241.     }   
  242.   
  243.     public long getElementCountInMemory() {   
  244.         try {   
  245.             return cache.getMemoryStoreSize();   
  246.         }   
  247.         catch (net.sf.ehcache.CacheException ce) {   
  248.             throw new CacheException( ce );   
  249.         }   
  250.     }   
  251.   
  252.     public long getElementCountOnDisk() {   
  253.         return cache.getDiskStoreSize();   
  254.     }   
  255.   
  256.     public Map toMap() {   
  257.         try {   
  258.             Map result = new HashMap();   
  259.             Iterator iter = cache.getKeys().iterator();   
  260.             while ( iter.hasNext() ) {   
  261.                 Object key = iter.next();   
  262.                 result.put( key, cache.get( key ).getObjectValue() );   
  263.             }   
  264.             return result;   
  265.         }   
  266.         catch (Exception e) {   
  267.             throw new CacheException( e );   
  268.         }   
  269.     }   
  270.   
  271.     public String toString() {   
  272.         return "EHCache(" + getRegionName() + ')';   
  273.     }   
  274.   
  275. }  
在这段代码中需要注意的是,很多的方法都是,不做任何处理就直接调用别的方法。如put方法和update方法是一个方法。系统这么设计是因为不做缓存的开发人员容易费解。其实缓存在实现上只有添加和删除方法。没有更新方法。更新就是替换。另外代码中有个toMap()方法,这个方法是采用的是HashMap作为存储对象。现在使用这个Map已经不是 性能最好的了。JDK1.5后出了一个ConcurrentMap,这个Map是线程安全的。不过仅仅是查询,用HashMap这个选择也不错。
  
 另外一件事情就是实现缓存提供者,实现CacheProvider接口。实现代码如下:
java 代码
  1. public class EhCacheProvider implements CacheProvider {

        private static final Log log = LogFactory.getLog(EhCacheProvider.class);

     private CacheManager manager;

        /**
         * Builds a Cache.
         *


         * Even though this method provides properties, they are not used.
         * Properties for EHCache are specified in the ehcache.xml file.
         * Configuration will be read from ehcache.xml for a cache declaration
         * where the name attribute matches the name parameter in this builder.
         *
         * @param name the name of the cache. Must match a cache configured in ehcache.xml
         * @param properties not used
         * @return a newly built cache will be built and initialised
         * @throws CacheException inter alia, if a cache of the same name already exists
         */
        public Cache buildCache(String name, Properties properties) throws CacheException {
         try {
                net.sf.ehcache.Cache cache = manager.getCache(name);
                if (cache == null) {
                    log.warn("Could not find configuration [" + name + "]; using defaults.");
                    manager.addCache(name);
                    cache = manager.getCache(name);
                    log.debug("started EHCache region: " + name);
                }
                return new EhCache(cache);
         }
            catch (net.sf.ehcache.CacheException e) {
                throw new CacheException(e);
            }
        }

        /**
         * Returns the next timestamp.
         */
        public long nextTimestamp() {
            return Timestamper.next();
        }

     /**
      * Callback to perform any necessary initialization of the underlying cache implementation
      * during SessionFactory construction.
      *
      * @param properties current configuration settings.
      */
     public void start(Properties properties) throws CacheException {
      if (manager != null) {
                log.warn("Attempt to restart an already started EhCacheProvider. Use sessionFactory.close() " +
                        " between repeated calls to buildSessionFactory. Using previously created EhCacheProvider." +
                        " If this behaviour is required, consider using net.sf.ehcache.hibernate.SingletonEhCacheProvider.");
                return;
            }
            try {
                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);
                }
            } catch (net.sf.ehcache.CacheException e) {
       //yukky! Don't you have subclasses for that!
       //TODO race conditions can happen here
       if (e.getMessage().startsWith("Cannot parseConfiguration CacheManager. Attempt to create a new instance of " +
                        "CacheManager using the diskStorePath")) {
                    throw new CacheException("Attempt to restart an already started EhCacheProvider. Use sessionFactory.close() " +
                        " between repeated calls to buildSessionFactory. Consider using net.sf.ehcache.hibernate.SingletonEhCacheProvider."
          , e );
                } else {
                    throw e;
                }
            }
     }

     private URL loadResource(String configurationResourceName) {
      URL url = ConfigHelper.locateConfig( configurationResourceName );
            if (log.isDebugEnabled()) {
                log.debug("Creating EhCacheProvider from a specified resource: "
                        + configurationResourceName + " Resolved to URL: " + url);
            }
            return url;
        }

     /**
      * Callback to perform any necessary cleanup of the underlying cache implementation
      * during SessionFactory.close().
      */
     public void stop() {
      if (manager != null) {
                manager.shutdown();
                manager = null;
            }
     }

     public boolean isMinimalPutsEnabledByDefault() {
      return false;
     }

  2. }

 实现这个接口就可以在配置文件中配置,表示Hibernate要采用这种类型的缓存。通过这个缓存我们也可以学到很多的东西。如start()方法。这个方法注释告诉我们,缓存的加载是在SessionFactory创建时加载的。为什么要在这个时候加载,把缓存和SessionFactory绑在一起。因为SessionFactory是二级缓存,Cache的处理级别也是二级缓存。 所以他们就搞到一块去了。再看看stop()方法,这个方法的注释也告诉我们一个很重要的信息,如果启用了Hibernate的二级缓存。在SessionFactory的创建期间是不会停止的。只有在SessionFactory关闭后,才可以启用停止方法。为什么呢?因为SessionFatory是线程安全的,Cache随便停止,对SessionFactory线程安全性是致命的。