`
fu80008
  • 浏览: 19178 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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

阅读更多

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

分享到:
评论
8 楼 fu80008 2007-11-07  
对于缓存的运用,大家可以去参考相关的书籍,在应用方面Hibernate可以说应用的非常的好,但对于对象数据模式,还是有很多的不足之处。这个议题暂时先发表到这里。等有了新发现再继续。
7 楼 fu80008 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没有研究过,不过吹得到挺厉害,如果有时间,大家可以去看看,呵呵!
6 楼 fu80008 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 + ')';
	}
5 楼 kenbli 2007-11-06  
写的很好亚,支持~~~

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

可以讲解一下缓存是怎么运作的吗?
4 楼 aflyer 2007-11-02  
呵呵!"其实缓存在实现上只有添加和删除方法。没有更新方法。更新就是替换."就看懂这句话!
3 楼 fu80008 2007-10-15  
实在不好意思,我的排版出现了问题。刚才修改了一下,你们先看着。我正在准备,在加上有工作,希望大家谅解。
2 楼 a3mao 2007-10-15  
继续啊
1 楼 fyting 2007-10-11  
帖子没发完整?

相关推荐

    hibernate二级缓存示例源码

    通过分析这些源码,我们可以更深入地理解Hibernate二级缓存的工作流程和实际应用。 ### 6. 注意事项 - 虽然二级缓存提高了效率,但过度依赖可能会导致数据一致性问题。因此,需要根据业务需求谨慎选择缓存策略。 -...

    Hibernate4二级缓存实例(源码)

    1. **Hibernate二级缓存**:二级缓存是Hibernate在一级缓存(Session级别的缓存)之外提供的全局共享缓存,可以跨Session共享数据。它允许多个并发用户访问相同的数据,减少对数据库的访问次数,提高系统性能。 2. ...

    hibernate 源码直接导入Eclipse

    - 第二级缓存:通过引入缓存插件(如 EhCache),提高数据访问性能。 - 异步批处理:使用批处理更新和查询,减少数据库交互次数。 - 动态加载策略:选择合适的懒加载和立即加载策略,避免N+1查询问题。 总之,...

    Hibernate性能优化:一级缓存

    同时,理解一级缓存的原理也有助于我们理解二级缓存和其他缓存解决方案,如Ehcache和Infinispan,以及它们在复杂分布式系统中的应用。 总之,Hibernate的一级缓存是提高应用程序性能的关键因素之一。理解其工作原理...

    Hibernat一级缓存(源码)

    3. 分布式缓存:在多节点环境中,可以考虑使用二级缓存(如 Ehcache 或 Infinispan)来实现跨节点的数据共享,提高系统性能。 总结: 一级缓存作为 Hibernate 的核心特性,极大地提高了数据访问效率。通过对源码的...

    精通hibernate源码ch2

    - 第二级缓存:可选的,跨Session共享,可以是第三方缓存服务如Ehcache。 - 查询缓存:存储查询结果,提高查询性能。 6. Hibernate源码解析: - SessionImpl:实际的Session实现,包含对数据库的CRUD操作。 - ...

    day37 05-HIbernate二级缓存:一级缓存更新同步到二级缓存及二级缓存配置文件

    学习和掌握Hibernate的二级缓存机制对于优化大型应用的性能至关重要。通过合理配置和使用,可以在不牺牲数据一致性的情况下,大幅度减少对数据库的访问,提高系统响应速度。在实际项目中,可以根据业务需求和系统...

    Hibernate一级缓存和二级缓存

    在深入理解Hibernate缓存机制时,查看源码可以帮助我们了解其实现细节。对于开发者来说,熟悉相关的调试工具如JVisualVM、JConsole等,能够监控缓存的使用情况,优化缓存配置。 **总结** Hibernate的一级缓存和二...

    hibernate二级缓存实例

    在这个"hibernate二级缓存实例"中,我们将深入探讨二级缓存的原理、配置以及在实际项目中的应用。 首先,我们需要了解一级缓存和二级缓存的区别。一级缓存是Session级别的,每个Session都有自己的一级缓存,用于...

    Spring4+Hibernate4二级缓存实例源码

    7. **注意事项**:二级缓存虽然能提升性能,但也可能导致数据一致性问题,因为缓存中的数据可能与数据库中的数据不同步。因此,在处理高并发和实时性要求高的场景时,需要谨慎使用二级缓存,并结合缓存更新策略来...

    Hibernate性能优化:二级缓存

    同时,为特定的实体或查询开启缓存: ```xml ... ``` 或者对于查询: ```xml SELECT ... FROM ... ``` ### 缓存策略 二级缓存的策略包括`read-only`、`read-write`、`nonstrict-read-write`和`...

    Hibernate源码

    Hibernate源码分析有助于深入理解其内部机制,提高开发效率,优化数据库操作性能。 1. **Hibernate核心模块(hibernate-core)** Hibernate的核心模块包含了ORM框架的主要功能,如实体管理、查询语言(HQL)、事件...

    精通Hibernate源码.rar

    本文将从多个角度探讨Hibernate源码中的核心概念和实现机制。 1. **持久化模型设计** Hibernate通过Java类和数据库表之间的映射(Mapping),实现了对象的持久化。在源码中,`hibernate.cfg.xml`配置文件定义了...

    hibernate源码 直接使用

    标题"hibernate源码 直接使用"表明我们将探讨的是Hibernate框架的源代码,以及如何直接在项目中应用这些源代码。Hibernate是一个流行的Java ORM(对象关系映射)框架,它简化了数据库操作,将数据库交互转化为面向...

    Hibernate源码解析(二)

    在本篇《Hibernate源码解析(二)》中,我们将深入探讨Hibernate这一强大的Java对象关系映射(ORM)框架的内部工作原理。这篇博客旨在帮助开发者更好地理解Hibernate的核心机制,以便于更高效地利用它来处理数据库...

    Hibernate源码解析(一)

    《Hibernate源码解析(一)》 在Java开发领域,Hibernate作为一款强大的对象关系映射(ORM)框架,极大地简化了数据库操作。深入理解Hibernate的源码,不仅可以帮助开发者更好地运用该工具,还能提升对Java编程和...

    hibernate一级缓存、二级缓存和查询缓存

    本文将详细讲解Hibernate中的三级缓存:一级缓存、二级缓存和查询缓存。 ### 1. 一级缓存 一级缓存是Hibernate内置的Session级别的缓存,也被称为事务性缓存。每当我们在Session中进行对象的CRUD(创建、读取、...

    传智播客hibernate源码

    标题"传智播客hibernate源码"暗示了这是一个关于Hibernate框架的源代码学习资源,可能包含了对Hibernate框架内部机制的深入解析,以及如何在实际项目中应用Hibernate的相关示例。 描述中的内容重复,进一步确认了这...

    Hibernate 二级缓存 总结整理

    通过理解和运用Hibernate的二级缓存,我们可以优化应用性能,减少数据库压力,但同时也需要注意缓存可能带来的问题,如数据一致性、内存管理和并发控制等。在实际项目中,结合业务需求和系统特点,合理配置和管理...

Global site tag (gtag.js) - Google Analytics