`

使用hibernate二级缓存优化你的应用

阅读更多

原文:http://www.devx.com/dbzone/article/29685/1954

作者:John Ferguson Smart

翻译及加工: 魏超

 

 

因为对hibernate缓存的不了解,新接触hiberante开发的人往往无法很好的使用它。然而,合理的使用缓存将成为加速hibernate程序的最有效途径。


 

 

频繁的数据库读写会影响web项目的性能表现。作为一个高性能的对象/关系持久化查询技术,单纯的使用hibernate可能还不能解决你所有的性能问题。很多时候,开启二级缓存将会很好的改变这种境况。下面的文章会让你对缓存有个初步的了解,同时告诉你怎么用缓存来提升性能。

 

 

什么是缓存(Cache)?

 

缓存被广泛应用的用于优化数据库。当一些数据被从数据库中读取出来的时候,我们可以把它们放到缓存里.这样,在再次用到这些数据的时候,我们就可以直接从缓存把他们取出来,而不是去连接数据库。当然,当数据库的记录被修改更新的时候,我们就需要把缓存清空掉。因为我们无从得知在数据库记录更新时,缓存中的记录是否还和数据库里的相同

 

 

Hibernate的缓存

Hibernate有一级和二级两种缓存对象。一级缓存关联session对象,而二级缓存关联着session工厂对象。默认情况下,一级缓存是在单个事务中使用的。举例来说,在同一个事务中,当一个对象在事务提交前,被修改了很多次,那么同过一级缓存,在事务提交的时候,我们就会把所有这些修改写在同一条SQL语句中传递给数据库,而不是每次修改都有一条语句。当然,我们这篇文章关注的是二级缓存。可以这么说,相比一级缓存,二级缓存是跨事务的,它将一个事务中产生的查询对象保存下来,在其他事务执行相同的查询的时候,这些被保存的对象就可以被直接拿出来使用,这样就能最大化的减少数据库操作。因此,对于同一个服务,只要有一个用户执行了某个查询,那么其他将要执行相同查询的用户都将从二级缓存中受益。

 

此外,相对于上面说的缓存持久化对象,你可以使用query-level的缓存来存储真正的查询结果集

 

 

缓存的实现

市场上提供了相当多的缓存技术的选择,既有开源的也有收费的。 Hibernate支持下面的开源缓存:

  • EHCache (org.hibernate.cache.EhCacheProvider)
  • OSCache (org.hibernate.cache.OSCacheProvider)
  • SwarmCache (org.hibernate.cache.SwarmCacheProvider)
  • JBoss TreeCache(org.hibernate.cache.TreeCacheProvider)

以下是不同的缓存产品的特点:

  • EHCache  是一个快速,轻巧,易于使用的线程内的高速缓存。它支持只读和读/写缓存,内存和基于磁盘的缓存。但是,它不支持群集。
  • OSCache 是另一个开源缓存解决方案,也提供对JSP页面和任意对象的缓存功能。它是一个强大和灵活的方案,如同EHCache,支持只读和读/写缓存,内存和基于磁盘的缓存。它还提供对JavaGroup或JMS集群的基础支持。
  • SwarmCache 是一个基于JavaGroup的简单集群缓存解决方案。它支持只读或不严格的读/写缓存(接下来的部分解释这个词)。此缓存的适用于读操作远多于写操作的数据库应用。
  • JBoss TreeCache的 是一个强大的复制(同步或异步)事务缓存。如果你需要一个真正有事务能力的缓存架构,选择这个缓存。

另一个值得一提的是商业缓存Tangosol: Tangosol的高速缓存的一致性.

 

 

缓存策略

一旦选择了缓存实现,你还需要指定你的访问策略。以下四个缓存策略可供选择:

  • 只读:用于只读取而不写入的访问策略。这是迄今为止最简单,效果最好的缓存策略。
  • 读/写:如果你有数据需要写入(更新),可以用这个缓存。当然,读/写缓存的开销要比只读缓存大。在非JTA的环境中,每次操作应在Session.close()或者Session.disconnect()被结束。
  • 非严格的读/写:这种策略并不能保证两个操作修改同一个数据的安全。因此,它适用于经常查询,而只是偶尔的修改的数据。
  • 事务性:这是一个完全的事务缓存,可用于JTA环境。

下表显示了可供选择的不同的缓存实现:

 

缓存 只读 非严格的读/写 读/写 事务
EHCache 是的 是的 是的 没有
OSCache 是的 是的 是的 没有
SwarmCache 是的 是的 没有 没有
JBoss TreeCache 是的 没有 没有 是的


下面的部分将展示在JVM中使用EHCache缓存。

 

 

缓存配置

要激活二级缓存,首先你需要定义hibernate.cfg.xml文件的hibernate.cache.provider_class:

 

 

<hibernate-configuration>
	<session-factory>
		...
		<property name="hibernate.cache.provider_class">
			org.hibernate.cache.EHCacheProvider
		</property>
		...
	</session-factory>
</hibernate-configuration>

 

 

当你Hibernate版本是3以上时,你可能还需要使用hibernate.cache.use_second_level_cache属性。这个属性让你可以激活(或停止)二级缓存。默认情况下,二级缓存使用的是EHCache并且已经激活。

 

一个例子

这个例子由几个简单的表组成:Airport, Employee, Language,Country. 每个employee属于一个country,会说多国的language. 而每个country有许多的airport. 下面2幅图分别是这4个类的类图和数据库表图。

 




 
 

设置一个只读缓存

首先设置国家(Country)的Hibernate映射类:

 

<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects">
    <class name="Country" table="COUNTRY" dynamic-update="true">
		<meta attribute="implement-equals">true</meta>    
 		<cache usage="read-only"/>

        <id name="id" type="long" unsaved-value="null" >
            <column name="cn_id" not-null="true"/>
            <generator class="increment"/>
        </id>

	   <property column="cn_code" name="code" type="string"/>
	   <property column="cn_name" name="name" type="string"/>

	  <set name="airports">
	   <key column="cn_id"/>
	   <one-to-many class="Airport"/>
	  </set>
    </class>
</hibernate-mapping>

 

 

如果这时候你要取所有的country列表,你可以在CountryDao中添加如下方法:

 

public class CountryDAO {
	...	
	public List getCountries() {
		return SessionManager.currentSession()
					   .createQuery(
					      "from Country as c order by c.name")
					   .list();
	}
}

 

 

 

因为上面的方法可能会被频繁的调用,所以我们需要了解它在压力下的性能表现。这里我们写一个单元测试来模拟5次成功的调用:

 

public void testGetCountries() {
		CountryDAO dao = new CountryDAO();
		for(int i = 1; i <= 5; i++) {
  		    Transaction tx = SessionManager.getSession().beginTransaction();
		    TestTimer timer = new TestTimer("testGetCountries");
		    List countries = dao.getCountries();
		    tx.commit();
		    SessionManager.closeSession();
		    timer.done();
		    assertNotNull(countries);
		    assertEquals(countries.size(),229);
		}
	}

 

 

你可以用你熟悉的IDE或者用Maven2的命令行来运行上面的代码。在这里我们连接了一个本地的MySQL数据库。当这段代码被成功的运行,你应该会看到下面的输出:

 

testGetCountries: 521 ms.
testGetCountries: 357 ms.
testGetCountries: 249 ms.
testGetCountries: 257 ms.
testGetCountries: 355 ms.

 

 

每次操作基本上要用4分之1秒。在多数情况下,上面我们取得的国家列表不会被频繁的修改。因此这是一个很好的例子来引入只读缓存(read-only Cache).

 

有2种方法可以激活二级缓存的类:

 

1. 在要使用二级缓存的那个类对应的hbm.xml文件中添加下面的属性:

<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects">
         <class name="Country" table="COUNTRY" dynamic-update="true">
		<meta attribute="implement-equals">true</meta>
		<cache usage="read-only"/>
            ...			        
        </class>
    </hibernate-mapping>

2. 或者把所有的缓存信息都记录在hibernate.cfg.xml文件中:

 

<hibernate-configuration>
	<session-factory>
		...
		<property name="hibernate.cache.provider_class">
			org.hibernate.cache.EHCacheProvider
		</property>
		...
		<class-cache 
class="com.wakaleo.articles.caching.businessobjects.Country"
usage="read-only"
		/>
	</session-factory>
</hibernate-configuration>

 

接下来,你需要配置这个类的缓存规则。这些规则会决定缓存的一些细节。我们在这个例子中用的是EHChe缓存。当然,在其他不同的缓存中,配置规则的方式是不一样的。

EHCache需要一个配置文件(在类路径的根目录,一般称为ehcache.xml)。在下面这个站点有这个文件的模板: EHCache模板 。基本上,你要为每一个你想做二级缓存的类在这个文件中配置规则。如果你没有配置,程序将调用一个默认的规则。

对于这个例子,我们可以使用下面的简单EHCache配置文件:

<ehcache>

    <diskStore path="java.io.tmpdir"/>

    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        diskPersistent="false"
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU"
        />
        
    <cache name="com.wakaleo.articles.caching.businessobjects.Country"
        maxElementsInMemory="300"
        eternal="true"
        overflowToDisk="false"
        />

</ehcache>

 

这个文件基本上建立了一个国家基于内存的缓存,最多300个元素(国家清单包含229个国家)。请注意,缓存永不过期(即'永恒=真正的财产)。

这个配置建立了一个基于内存的country缓存。里面最多可以保存300个元素(在我们这个例子中,查询出的结果集包含229个country).(注:这个缓存被配置为永远不过期。eternal=true)

现在,重新运行测试,结果如下:

 

testGetCountries: 412 ms.
testGetCountries: 98 ms.
testGetCountries: 92 ms.
testGetCountries: 82 ms.
testGetCountries: 93 ms.

 

可以明显的看出,操作时间提高到了10分之1秒左右(第一次用了400多ms是因为第一次真正和数据库交互了)

 

缓存内部的存储

在继续下去之前,我们有必要先来了解在上面的代码背后发生了什么。值得注意的是,hibernate缓存并不存储对象的实例(Instances).实际上它存储的是对象的“dehydrated”格式(hibernate术语),这种格式其实就是一系列的属性值。下面是country缓存中内容的一个例子:

 

{ 
  30  => [bw,Botswana,30], 
  214 => [uy,Uruguay,214], 
  158 => [pa,Panama,158],
  31  => [by,Belarus,31]
  95  => [in,India,95]
  ...
}

 注意,每一个Id映射到一系列的属性上。这里你可能会发现,只有原始的属性被缓存下来了,具体来说,airport属性没有被缓存。实际上,这是因为airport属性是一个association:是一系列指向其他持久化对象的引用。

 

默认情况下,hibernate不会对associations进行缓存。当然,你可以设置是否缓存association,同时可以设置当二级缓存中的对象被重新加载出来的时候哪些associations要被检索。

 

Association缓存是一个非常强大的功能。下面我们将对它进行更详细的研究。

 

 

使用Associations缓存

假设你需要获得一个country对应的所有employees的列表(包括employee名字, language等),你需要添加下面的映射配置:

 

<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects">
    <class name="Employee" table="EMPLOYEE" dynamic-update="true">
		<meta attribute="implement-equals">true</meta>    

       <id name="id" type="long" unsaved-value="null" >
            <column name="emp_id" not-null="true"/>
            <generator class="increment"/>
       </id>

	 <property column="emp_surname" name="surname" type="string"/>
	 <property column="emp_firstname" name="firstname" type="string"/>
	   
	 <many-to-one name="country"
 	              column="cn_id"
	              class="com.wakaleo.articles.caching.businessobjects.Country"  
			  not-null="true" />
			    
	 <!-- Lazy-loading is deactivated to demonstrate caching behavior -->    
	 <set name="languages" table="EMPLOYEE_SPEAKS_LANGUAGE" lazy="false">
    	 <key column="emp_id"/>
      	    	<many-to-many column="lan_id" class="Language"/>
	 </set>				        
    </class>
</hibernate-mapping>

 假如你真的需要在每次取得employee对象的时候都加载其对应的language信息,你需要把language属性的lazy设置为false.(不延迟加载)(这里只是为了举例的需要,在真实情况下,不建议取消延迟加载,因为这会影响性能)。同时,你需要添加下面的方法来获得employee:

 

public class EmployeeDAO {

	public List getEmployeesByCountry(Country country) {
		return SessionManager.currentSession()
		 .createQuery(
		      "from Employee as e where e.country = :country "
                + " order by e.surname, e.firstname")
		 .setParameter("country",country)
		 .list();
	}
}

 

接着,写一个单元测试:

 

public class EmployeeDAOTest extends TestCase {

	CountryDAO countryDao = new CountryDAO();
	EmployeeDAO employeeDao = new EmployeeDAO();

	/**
	 * Ensure that the Hibernate session is available
	 * to avoid the Hibernate initialisation interfering with
	 * the benchmarks
	 */
	protected void setUp() throws Exception {		
		super.setUp();
		SessionManager.getSession();
	}

	public void testGetNZEmployees() {
		TestTimer timer = new TestTimer("testGetNZEmployees");
		Transaction tx = SessionManager.getSession().beginTransaction();
		Country nz = countryDao.findCountryByCode("nz");
		List kiwis = employeeDao.getEmployeesByCountry(nz);
		tx.commit();
		SessionManager.closeSession();
		timer.done();
	}

	public void testGetAUEmployees() {
		TestTimer timer = new TestTimer("testGetAUEmployees");
		Transaction tx = SessionManager.getSession().beginTransaction();
		Country au = countryDao.findCountryByCode("au");
		List aussis = employeeDao.getEmployeesByCountry(au);	
		tx.commit();
		SessionManager.closeSession();
		timer.done();
	}

	public void testRepeatedGetEmployees() {
		testGetNZEmployees();
		testGetAUEmployees();
		testGetNZEmployees();
		testGetAUEmployees();
	}
}

 

运行这个单元测试,可以看到下面的结果:

 

testGetNZEmployees: 1227 ms.
testGetAUEmployees: 883 ms.
testGetNZEmployees: 907 ms.
testGetAUEmployees: 873 ms.
testGetNZEmployees: 987 ms.
testGetAUEmployees: 916 ms.

可以看出,每次为一个country取得50几个employee都需要差不多1秒钟 。这样的速度太慢了。这是一个传统的"N+1"问题。如果启用SQL日志,我们可以看到,每次执行一个employee的查询语句后面,都跟着数百条language表的查询。每次我们从缓存中加载employee对象,其关联的全部language都会被重新检索。我们应该怎么改善这里的性能呢?首先,激活employee类的读写缓存(read/write cache):

 

<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects">
        <class name="Employee" table="EMPLOYEE" dynamic-update="true">
		<meta attribute="implement-equals">true</meta>
		<cache usage="read-write"/>
            ...			        
        </class>
    </hibernate-mapping>

同时,我们要激活language上的只读缓存(read-only cache):

 

<class name="Language" table="SPOKEN_LANGUAGE" dynamic-update="true">
		<meta attribute="implement-equals">true</meta>    
		<cache usage="read-only"/>
            ...			        
        </class>
    </hibernate-mapping>

 之后,在ehcache.xml文件中添加下面2个缓存规则:

 

<cache name="com.wakaleo.articles.caching.businessobjects.Employee"
        maxElementsInMemory="5000"
        eternal="false"
        overflowToDisk="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
    />
    <cache name="com.wakaleo.articles.caching.businessobjects.Language"
        maxElementsInMemory="100"
        eternal="true"
        overflowToDisk="false"
    />

 到这里,"N+1"问题依然没有被解决:每次加载employee,50条以上的查询依旧会被执行。因为,我们还需要激活employee.hbm.xml文件中的language的association.

 

<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects">
    <class name="Employee" table="EMPLOYEE" dynamic-update="true">
		<meta attribute="implement-equals">true</meta>    

      <id name="id" type="long" unsaved-value="null" >
            <column name="emp_id" not-null="true"/>
            <generator class="increment"/>
      </id>

	<property column="emp_surname" name="surname" type="string"/>
	<property column="emp_firstname" name="firstname" type="string"/>
	   
	<many-to-one name="country"
 	  		 column="cn_id"
 		       class="com.wakaleo.articles.caching.businessobjects.Country"  
			not-null="true" />
			    
	<!-- Lazy-loading is deactivated to demonstrate caching behavior -->    
      <set name="languages" table="EMPLOYEE_SPEAKS_LANGUAGE" lazy="false">
	    <cache usage="read-write"/>
   	    <key column="emp_id"/>
    	    <many-to-many column="lan_id" class="Language"/>
	</set>    					        
    </class>
</hibernate-mapping>

 这样配置之后,我们可以看到下面的结果(速度提高了10倍左右):

 

testGetNZEmployees: 1477 ms.
testGetAUEmployees: 940 ms.
testGetNZEmployees: 65 ms.
testGetAUEmployees: 65 ms.
testGetNZEmployees: 76 ms.
testGetAUEmployees: 52 ms.

 

 

使用查询缓存(Query Caches)

在某些情况下,我们需要缓存的是确切的查询结果集,而不是某些对象。例如,每次调用getCountries()方法的时候,我们都会得到相同的country列表。因此,除了缓存country类,我们还需要缓存查询结果集本身。

 

为了实现这个,我们需要启用hibernate.cfg.xml文件中的hibernate.cache.use_query_cache属性:

 

<property name="hibernate.cache.use_query_cache">true</property>

接着,在你需要缓存查询结果的地方使用setCacheable()方法:

 

public class CountryDAO {

    public List getCountries() {
        return SessionManager.currentSession()
                             .createQuery("from Country as c order by c.name")
				     .setCacheable(true)
                             .list();
    }
}

 为了保证缓存结果的正确性,每当被缓存的数据在应用中被修改的时候,这些查询缓存的结果在Hibernate中就过期了(hibernate会重新刷新这部分缓存)。然而,hibernate却无法获悉本身应用之外的,其他应用直接去修改数据库数据的操作。因此,如果你使用的数据会频繁的处于提交更新的状态下,你就不应该使用任何的二级缓存。如果非要使用的话,至少,你应该将二级缓存的超时时间设置的足够短。  

 

正确的Hibernate缓存

缓存是一个强大的技术,Hibernate提供了一个有效,灵活且过度缓和的方式来实现它。即使是默认的配置也可以在许多简单的应用中有效的提高性能。然而,如同其他强大的工具一样,hibernate需要更深入的思考和微调,来取得最佳的效果。而缓存——如同其他的优化技术——应该遵循增量的,测试驱动的方法。当合理使用的时候,少量的缓存就可以使你程序发挥出最大的效能。

 

 

  • 大小: 24.9 KB
  • 大小: 54 KB
分享到:
评论
2 楼 nneverwei 2011-01-05  
tangwenchao86 写道
写得真好,你都是在哪看到的啊?

是我翻译及修改的,原文英文地址在本文开头的那个链接里
1 楼 tangwenchao86 2010-12-30  
写得真好,你都是在哪看到的啊?

相关推荐

    hibernate二级缓存实例

    总的来说,"hibernate二级缓存实例"是一个很好的学习资源,它可以帮助我们理解二级缓存的工作机制,掌握如何在项目中配置和使用,以及注意潜在的问题和优化策略。通过实践,我们可以更好地运用这一技术,提升Java...

    hibernate一级缓存和二级缓存的区别与联系

    总结来说,Hibernate 的一级缓存和二级缓存都是为了提高数据访问效率,但它们在范围和并发控制方面有所不同。一级缓存是事务级别的,保证了数据的强一致性,而二级缓存提供了更多的灵活性,可以跨事务共享,但需要...

    hibernate二级缓存示例源码

    综上所述,通过学习`hibernate二级缓存示例源码`,我们可以了解到如何在实际项目中配置和使用Hibernate二级缓存,从而提升系统的性能。在实际应用中,应结合具体场景选择合适的缓存策略,以达到最佳的性能优化效果。

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

    Hibernate的一级缓存、二级缓存和查询缓存共同构建了一个层次化的缓存体系,有效地缓解了数据库的压力,提升了应用的运行效率。理解并掌握这些缓存机制,对于优化Hibernate应用至关重要。在实践中,合理配置和管理...

    hibernate二级缓存包

    Hibernate二级缓存是Java开发中使用Hibernate框架进行数据持久化时优化性能的一种重要技术。它在一级缓存(Session级别的缓存)的基础上,提供了一个全局的、跨会话的数据存储层,可以显著减少对数据库的访问,从而...

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

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

    Hibernate一级缓存和二级缓存

    **二、Hibernate二级缓存** 二级缓存是SessionFactory级别的,跨越了多个Session,可以被多个线程共享。它通常由第三方插件如EhCache、Infinispan等提供。二级缓存分为以下几种类型: 1. **集合缓存**:用于存储...

    Hibernate 二级缓存 总结整理

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

    hibernate二级缓存java包下载

    二级缓存是 Hibernate 缓存策略的一部分,它在应用程序的多个会话之间共享数据,进一步优化了数据库访问效率。 二级缓存分为以下关键知识点: 1. **一级缓存与二级缓存的区别**: - 一级缓存:每个 Hibernate ...

    springboot+jpa(hibernate配置redis为二级缓存) springboot2.1.4

    通过以上步骤,我们就成功地在Spring Boot 2.1.4.RELEASE项目中配置了使用Redis作为Hibernate二级缓存的环境。这将显著提升数据库查询效率,减少对数据库的压力,尤其在高并发场景下,效果尤为明显。记得在实际生产...

    Spring集成的Hibernate配置二级缓存

    在企业级Java应用开发中,Spring和...总之,合理利用Hibernate的二级缓存机制,结合Spring的管理能力,可以有效地提升Java应用的性能。通过优化缓存配置和策略,可以在不牺牲数据一致性的情况下,达到良好的用户体验。

    hibernate一级和二级缓存配置与详解

    本篇将深入探讨Hibernate的一级缓存和二级缓存,以及查询缓存的配置和使用。 ### 一级缓存 一级缓存是Hibernate默认提供的缓存,它是Session级别的,每个Hibernate Session都有一个私有的、本地的一级缓存。当我们...

    hibernate二级缓存所需要的 jar包

    本篇将详细介绍Hibernate二级缓存的概念、作用以及所需jar包的作用。 一、Hibernate二级缓存概念 Hibernate的一级缓存是指Session级别的缓存,每个Session内部都有一个一级缓存,用于存储实体对象,当Session关闭时...

    hibernate 二级缓存

    本篇文章将深入探讨Hibernate二级缓存的概念、工作原理以及如何在实际项目中设置和使用。 **一、二级缓存概念** 一级缓存是每个Hibernate Session内部的一个内存区域,用于存储Session期间的操作对象。当Session...

    hibernate二级缓存要导入的包

    本压缩包提供的资源应该包含了实现Hibernate二级缓存所需的关键组件和库文件。 一级缓存是Hibernate Session内的缓存,它是每个Session实例独有的,当Session关闭时,一级缓存中的数据也会被清除。而二级缓存则是一...

    Hibernate一级缓存、二级缓存以及查询缓存实例

    本文将深入探讨Hibernate的一级缓存、二级缓存以及查询缓存,通过具体的实例来阐述它们的工作原理和使用方法。 首先,我们从一级缓存开始。一级缓存是Hibernate默认提供的缓存,它是每个Session级别的,也被称为...

    hibernate二级缓存(包括注解方式)

    标题“hibernate二级缓存(包括注解方式)”指出了本文将探讨的是Hibernate框架中的二级缓存机制,并且会涉及使用注解的方式进行配置。Hibernate是一个流行的对象关系映射(ORM)框架,它允许开发者在Java应用中使用...

    Hibernate4二级缓存实例(源码)

    这里它被用作Hibernate二级缓存的实现方式,这意味着当数据首次从数据库中读取后,会被存储在memcached中,后续请求可以直接从缓存中获取,避免了频繁的数据库交互,从而提高了系统的响应速度。 **知识点详解:** ...

    hibernate二级缓存 SSH

    Hibernate二级缓存是一个优化策略,它能提高数据访问性能,减少对数据库的直接访问。 首先,Struts2作为MVC(模型-视图-控制器)框架,负责处理用户请求和转发到相应的业务逻辑。Spring框架则提供了依赖注入和事务...

Global site tag (gtag.js) - Google Analytics