论坛首页 Java企业应用论坛

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

浏览 8433 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-10-11  

        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线程安全性是致命的。

   发表时间:2007-10-11  
帖子没发完整?
0 请登录后投票
   发表时间:2007-10-15  
继续啊
0 请登录后投票
   发表时间:2007-10-15  
实在不好意思,我的排版出现了问题。刚才修改了一下,你们先看着。我正在准备,在加上有工作,希望大家谅解。
0 请登录后投票
   发表时间:2007-11-02  
呵呵!"其实缓存在实现上只有添加和删除方法。没有更新方法。更新就是替换."就看懂这句话!
0 请登录后投票
   发表时间:2007-11-06  
写的很好亚,支持~~~

可能水平有限,很多地方还是不太懂呢...

可以讲解一下缓存是怎么运作的吗?
0 请登录后投票
   发表时间:2007-11-07  
Cache因为大家不注意,所以对它往往会产生一种阴影。其实原理非常的简单。从根本上来说,就是需要一个能存能取得容器。针对楼上的问题我下面介绍一种Hibernate提供的更简单的cache-HashtableCache。这个Cache用Java的Hashtable映射变量来实现。下面先看一下代码:
public class HashtableCache implements Cache {
	
	private final Map hashtable = new Hashtable();
	private final String regionName;
	
	public HashtableCache(String regionName) {
		this.regionName = regionName;
	}

	public String getRegionName() {
		return regionName;
	}

	public Object read(Object key) throws CacheException {
		return hashtable.get(key);
	}

	public Object get(Object key) throws CacheException {
		return hashtable.get(key);
	}

	public void update(Object key, Object value) throws CacheException {
		put(key, value);
	}
	
	public void put(Object key, Object value) throws CacheException {
		hashtable.put(key, value);
	}

	public void remove(Object key) throws CacheException {
		hashtable.remove(key);
	}

	public void clear() throws CacheException {
		hashtable.clear();
	}

	public void destroy() throws CacheException {

	}

	public void lock(Object key) throws CacheException {
		// local cache, so we use synchronization
	}

	public void unlock(Object key) throws CacheException {
		// local cache, so we use synchronization
	}

	public long nextTimestamp() {
		return Timestamper.next();
	}

	public int getTimeout() {
		return Timestamper.ONE_MS * 60000; //ie. 60 seconds
	}

	public long getSizeInMemory() {
		return -1;
	}

	public long getElementCountInMemory() {
		return hashtable.size();
	}

	public long getElementCountOnDisk() {
		return 0;
	}
	
	public Map toMap() {
		return Collections.unmodifiableMap(hashtable);
	}

	public String toString() {
		return "HashtableCache(" + regionName + ')';
	}
0 请登录后投票
   发表时间:2007-11-07  
以上代码非常的简单,开始先声明一个容器:
private final Map hashtable = new Hashtable();
以后就是对一些接口的实现。但总的来说就是对map的简单的增加、删除或者取值。原理就是如此。在运行Hibernate时,Hibernate根据不同Cache的特点而采用不同的Cache,当然大部分是可以根据不同的情况选择的。
  在所有的cache中,有一个cache也是我们需要注意的,那就是JndiBoundTreeCacheProvider。这个Cache是专门为Jboss准备的,因为Hibernate已经属于Jboss公司了,所以Jboss总会搞点特权。这个Cashe也是Hibernate唯一实现的JNDI类型的Cache,采用了TreeCache来实现的。对于TreeCache没有研究过,不过吹得到挺厉害,如果有时间,大家可以去看看,呵呵!
0 请登录后投票
   发表时间:2007-11-07  
对于缓存的运用,大家可以去参考相关的书籍,在应用方面Hibernate可以说应用的非常的好,但对于对象数据模式,还是有很多的不足之处。这个议题暂时先发表到这里。等有了新发现再继续。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics