- 浏览: 209315 次
- 性别:
- 来自: 北京
最新评论
-
Relucent:
likaiyihou51 写道The constructor ...
获得Hibernate Criteria实际SQL的方法 -
likaiyihou51:
The constructor CriteriaJoinWal ...
获得Hibernate Criteria实际SQL的方法 -
likaiyihou51:
大侠 我copy了这个代码 又个问题,能给看一下吗
获得Hibernate Criteria实际SQL的方法 -
haisee:
管用,Firefox和Chrom验证通过。
Javascript设置和获取Textarea的光标位置的方法 -
wanghaosvse:
请问楼主,有没有跟数据库同步的动态加载
带选择框的JS树控件2 (为JSTree再次提速)
本文转载自:http://vinnyz.iteye.com/blog/248607
原文题目:Speed Up Your Hibernate Application with Second-Level Caching 原文来源:http://www.devx.com/dbzone/Article/29685/1954 作者简介:John Ferguson Smart,参与过很多企业和政府大型的的J2EE项目,他的专长包括J2EE的架构,开发和IT项目管理。他也有很多的在JAVA的开源技术方面的经验。这是他技术blog的链接www.jroller.com/page/wakaleo 原文翻译如下: 通过二级缓存来加快你的hibernate应用程序 新的hibernate开发人员有时并不知道hibernate的缓存结果并没有充分的使用它,尽管如此,当我们正确使用缓存的时候,它能够变为加速hibernate应用程序最有力的武器之一。 在web应用程序中和大数据量的数据库交互经常导致性能问题。hibernate是一种高性能的,提供对象/关系持久化和查询的服务,但是如果没有帮助就不会解决所有性能上的问题。在很多情况下,二级缓存就是hibernate潜在的需要来实现所有的性能上的处理。这篇文章将研究hibernate的缓存功能并且展现怎么使用才能明显的提升应用程序的性能。 缓存介绍 缓存被广泛用于数据库应用领域。缓存的设计就是为了通过存储已经从数据库读取的数据来减少应用程序和数据库之间的数据流量,而数据库的访问只在检索的数据不在当前缓存的时候才需要。如果数据库以一些方式进行更新和修改的话,那么应用程序可能需要每隔一段时间清空缓存,因为它没有方法知道缓存里的数据是不是最新的。 Hibernate缓存 从对象来说,hibernate用了两种缓存的方法:一级缓存和二级缓存。一级缓存和Session对象关联,二级缓存和Session Factory对象关联。默认情况下,hibernate使用一级缓存来作为一次事务预处理的基础。hibernate使用它主要是为了减少在一次给定的事务中需要被生成的SQL查询语句。例如,一个对象在同一个事务中被修改好几次,hibernate会在事务结束的时候只生成一条SQL更新语句,它包含了所有修改内容。这篇文章主要介绍二级缓存。为了减少和数据库的交互,二级缓存保存已经读出的对象在各个事务之间的Session Factory级别。这些对象在整个程序中都可以得到,而不仅是用户运行查询的时候。这种方式,每次查询返回的对象都已经被载入了缓存,一次或多次潜在的数据库事务可以避免。 除此之外,你可以使用查询级别缓存如果你需要缓存的实际的查询结果,而不仅仅是持久化对象。 缓存的实现 缓存是软件中复杂的部分,市场上也提供了相当数量的选择,包括基于开源的和商业的。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一样,也支持只读和读写缓存,基于内存和硬盘的数据储存。它通过JavaGroups或者JMS也提供了对簇的基本支持。 SwarmCache是一种基于簇的解决方案,它本身也基于集群服务实体间通信的通信协议。它提供了只读和没有限制的读写缓存(下一部分将解释这个名词)。这种类型的缓存对那些典型的读操作比写操作多的多的应用程序是适合的。 JBoss TreeCache是一种强大的两重性(同步的或异步的)和事务性的缓存。使用此实现如果你确实需要一种事务能力的缓存架构。 另一种值得提及的缓存实现是商业化的Tangosol Coherence cache。 缓存策略 一旦你选择了你的缓存实现,你需要指定你的缓存策略。以下是四种缓存策略: 只读:这种策略对那种数据被频繁读取但是不更新是有效的,这是到目前为止最简单,表现最好的缓存策略。 读写:读写缓存对假设你的数据需要更新是必要的。但是读写需要比只读缓存花费更多的资源。在非JTA的事务环境中,每一个事务需要完成在Session.close() 或Session.disconnect()被调用的时候。 没有限制的读写:这个策略不能保证2个事务不会同时的修改相同的数据。因此,它可能对那些数据经常被读取但只会偶尔进行写操作的最适合。 事务性:这是个完全事务性的缓存,它可能只能被用在JTA环境中。 对每一个缓存实现来说,支持策略不是唯一的。图一展现了对 缓存实现可供的选择。 文章余下的部分用来展示一个使用EHCache单一的JVM缓存。 缓存配置 为了启用二级缓存,我们需要在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> 为了在hibernate3中测试的目的,你还要使用hibernate.cache.use_second_level_cache属性,它可以让你启用(和关闭)二级缓存。默认情况下,二级缓存是启用的同时使用EHCache。 一个实际应用 这个例子演示的程序包含四张简单的表:国家列表,机场列表,员工列表,语言列表。每个员工被分配了一个国家,并且能说很多语言。每个国家能有任意数量的机场。 图一展现了UML类图 图二展现了数据库架构 这个例子的源代码(http://assets.devx.com/sourcecode/14239.tgz)包括了以下的SQL脚本,你需要用它创建和实例化数据库。 * src/sql/create.sql: 创建数据库的SQL脚本 * src/sql/init.sql: 测试数据 安装Maven2 在写的时候,Maven2安装目录看来好像缺少了一些jars。为了解决这个问题,在应用程序源码的根目录里找到那些缺少的jars。把它们安装到Maven2的文件里,到应用程序的目录下,执行以下的命令 $ mvn install:install-file -DgroupId=javax.security -DartifactId=jacc -Dversion=1.0 -Dpackaging=jar -Dfile=jacc-1.0.jar $ mvn install:install-file -DgroupId=javax.transaction -DartifactId=jta -Dversion=1.0.1B -Dpackaging=jar -Dfile=jta-1.0.1B.jar 建立一个只读缓存 从简单的开始,下面是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>
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的命令行(演示程序提供了2个Maven2的工程文件)。这个演示程序通过本地的mysql来测试。当你运行这个测试的时候,应该得到类似以下的一些信息: $mvn test -Dtest=CountryDAOTest ... testGetCountries: 521 ms. testGetCountries: 357 ms. testGetCountries: 249 ms. testGetCountries: 257 ms. testGetCountries: 355 ms. [surefire] Running com.wakaleo.articles.caching.dao.CountryDAOTest [surefire] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 3,504 sec 可以看出每次调用大概花费半秒的时间,对大多数标准来说还是有点迟缓的。国家的列表很可能不是经常的改变,所以这个类可以作为只读缓存一个好的候选。所以加上去 你可以启用二级缓存用以下两种方法的任意一种 1.你可以在*.hbm.xml里启用它,使用cache的属性 <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文件中,使用class-cache属性 <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> 下一步,你需要为这个类设置缓存规则,这些规则决定了缓存怎么表现的细节。这个例子的演示是使用EHCache,但是记住每一种缓存实现是不一样的。 EHCache需要一个配置文件(通常叫做ehcache.xml)在类的根目录。EHCache配置文件的详细文档可以看这里(http://ehcache.sourceforge.net/documentation)。基本上,你要为每个需要缓存的类定义规则,以及一个defaultCache在你没有明确指明任何规则给一个类的时候使用。 对第一个例子来说,你可以使用下面简单的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> 这个文件为countries类建立一个基于内存最多300单位的缓存(countries类包含了229个国家)。注意缓存不会过期('eternal=true'属性)。现在通过返回的结果看下缓存的表现 $mvn test -Dtest=CompanyDAOTest ... testGetCountries: 412 ms. testGetCountries: 98 ms. testGetCountries: 92 ms. testGetCountries: 82 ms. testGetCountries: 93 ms. [surefire] Running com.wakaleo.articles.caching.dao.CountryDAOTest [surefire] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 2,823 sec 正如你期盼的那样,第一次查询没有改变因为需要加载数据。但是,随后的几次查询就快多了。 后台 在我们继续之前,看下后台发生了什么非常有用。一件事情你需要知道的是hibernate缓存不储存对象实例,代替的是它储存对象“脱水”的形式(hibernate的术语),也就是作为一系列属性值。以下是一个countries缓存例子的内容 { 30 => [bw,Botswana,30], 214 => [uy,Uruguay,214], 158 => [pa,Panama,158], 31 => [by,Belarus,31] 95 => [in,India,95] ... } 注意每个ID是怎么样映射到拥有属性值的数组的。你可能也注意到了只有主要的属性被储存了,而没有airports属性,这是因为airports属性只是一个关联:对其他持久化对象一系列的引用。 默认情况下,hibernate不缓存关联。而由你决定来缓存哪个关联,哪个关联需要被重载当缓存对象从二级缓存获得的时候。 关联缓存是一个非常强大的功能。下一部分我们将介绍更多的内容。 和关联缓存一起工作 假设你需要显示一个给定的国家的所有的员工(包括员工的名字,使用的语言等)。以下是employee类的hibernate映射 <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对象的时候需要得到一个员工会说的语言。强逼hibernate自动载入关联的languages集合,你设置了lazy属性为false()。你还需要一个DAO类来得到所有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(); } } 如果你运行上面的代码,你会得到类似以下的一些数据 $mvn test -Dtest=EmployeeDAOTest ... testGetNZEmployees: 1227 ms. testGetAUEmployees: 883 ms. testGetNZEmployees: 907 ms. testGetAUEmployees: 873 ms. testGetNZEmployees: 987 ms. testGetAUEmployees: 916 ms. [surefire] Running com.wakaleo.articles.caching.dao.EmployeeDAOTest [surefire] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 3,684 sec 所以对一个国家载入大约50个员工需要花费大约一秒的时间。这种方法显然太慢了。这是典型的N+1的查询问题。如果你启用SQL日志,你会发现对employee表的一次查询,紧跟着对language表几百次的查询,无论什么时候hibernate从缓存里得到一个employee对象,它都会重载所有关联的language。那怎么提升它的性能呢?第一件要做的事就是对employee启用读写缓存,如下 <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类启用缓存。只读缓存如下 <hibernate-mapping package="com.wakaleo.articles.caching.businessobjects"> <class name="Language" table="SPOKEN_LANGUAGE" dynamic-update="true"> <meta attribute="implement-equals">true</meta> <cache usage="read-only"/> ... </class> </hibernate-mapping> 然后你需要配置缓存的规则通过加入以下的内容到ehcache.xml文件中 <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启用缓存,如下 <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> 通过这个配置,你就能得到几近最优的性能 $mvn test -Dtest=EmployeeDAOTest ... testGetNZEmployees: 1477 ms. testGetAUEmployees: 940 ms. testGetNZEmployees: 65 ms. testGetAUEmployees: 65 ms. testGetNZEmployees: 76 ms. testGetAUEmployees: 52 ms. [surefire] Running com.wakaleo.articles.caching.dao.EmployeeDAOTest [surefire] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 0,228 sec 查询缓存 在确定的情况下,缓存一次查询的正确结果是非常有用的,不仅是只是个确定的对象。例如,getCountries()方法在每次调用的时候或许会返回相同的国家列表。所以,除了缓存country类之外,你也应该缓存查询结果本身。 为了实现这个目标,你需要在hibernate.cfg.xml文件中设置hibernate.cache.use_query_cache属性为true,如下 <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二级缓存实例"是一个很好的学习资源,它可以帮助我们理解二级缓存的工作机制,掌握如何在项目中配置和使用,以及注意潜在的问题和优化策略。通过实践,我们可以更好地运用这一技术,提升Java...
总结来说,Hibernate 的一级缓存和二级缓存都是为了提高数据访问效率,但它们在范围和并发控制方面有所不同。一级缓存是事务级别的,保证了数据的强一致性,而二级缓存提供了更多的灵活性,可以跨事务共享,但需要...
Hibernate二级缓存是一种提高应用程序性能的技术,它将数据存储在SessionFactory级别的缓存中,使得数据可以在不同的Session之间共享。这与一级缓存(Session级别)不同,一级缓存仅存在于单个Session生命周期内,当...
综上所述,通过学习`hibernate二级缓存示例源码`,我们可以了解到如何在实际项目中配置和使用Hibernate二级缓存,从而提升系统的性能。在实际应用中,应结合具体场景选择合适的缓存策略,以达到最佳的性能优化效果。
Hibernate 二级缓存是针对SessionFactory级别的全局缓存,与一级缓存(Session级别)不同,一级缓存只在单个Session生命周期内有效。二级缓存则允许不同Session之间共享数据,提高了数据访问效率,减少了对数据库的...
以下是关于Hibernate二级缓存与Ehcache的详细知识点: 1. **二级缓存的作用**:一级缓存是每个Session内部的缓存,用于存储Session内的对象,而二级缓存则是一个全局范围的缓存,它可以在多个Session之间共享,减少...
**hibernate一级缓存、二级缓存和查询缓存** 在Java的持久化框架Hibernate中,缓存机制是提高应用程序性能的关键要素。缓存能够减少数据库的访问次数,提高数据读取速度,并且在一定程度上降低了系统的负载。本文将...
学习和掌握Hibernate的二级缓存机制对于优化大型应用的性能至关重要。通过合理配置和使用,可以在不牺牲数据一致性的情况下,大幅度减少对数据库的访问,提高系统响应速度。在实际项目中,可以根据业务需求和系统...
Hibernate 二级缓存
Hibernate 二级缓存是一种高效的数据存储机制,它能够显著提升Web应用的性能,尤其是在处理大量数据流动时。缓存的主要目标是减少应用与数据库之间的交互次数,从而降低延迟并减轻数据库服务器的压力。在Hibernate...
Hibernate 是一个非常流行的开源对象关系映射(ORM)框架,它允许 Java 开发人员将数据库操作转换...通过学习这些内容,开发者可以更好地理解并掌握如何在实际项目中有效利用 Hibernate 的二级缓存功能,提升系统性能。
**二、Hibernate二级缓存** 二级缓存是SessionFactory级别的,跨越了多个Session,可以被多个线程共享。它通常由第三方插件如EhCache、Infinispan等提供。二级缓存分为以下几种类型: 1. **集合缓存**:用于存储...
**Hibernate 二级缓存总结整理** 在Java的持久化框架中,Hibernate是一个广泛使用的ORM(对象关系映射)工具,它极大地简化了数据库操作。在处理大数据量或高并发的场景下,为了提高性能和减少数据库负载,...
通过以上步骤,我们就成功地在Spring Boot 2.1.4.RELEASE项目中配置了使用Redis作为Hibernate二级缓存的环境。这将显著提升数据库查询效率,减少对数据库的压力,尤其在高并发场景下,效果尤为明显。记得在实际生产...
以EhCache为例,我们需要在项目中引入ehcache-core或ehcache的依赖,并在Hibernate配置文件(hibernate.cfg.xml或persistence.xml)中启用二级缓存,添加如下配置: ```xml <property name="hibernate.cache.use_...
2. **配置Hibernate**:在Hibernate的配置文件`hibernate.cfg.xml`中启用二级缓存并指定缓存提供商。以下是一个使用Ehcache的示例: ```xml <property name="hibernate.cache.use_second_level_cache">true ...
本篇将详细介绍Hibernate二级缓存的概念、作用以及所需jar包的作用。 一、Hibernate二级缓存概念 Hibernate的一级缓存是指Session级别的缓存,每个Session内部都有一个一级缓存,用于存储实体对象,当Session关闭时...
本篇文章将深入探讨Hibernate二级缓存的概念、工作原理以及如何在实际项目中设置和使用。 **一、二级缓存概念** 一级缓存是每个Hibernate Session内部的一个内存区域,用于存储Session期间的操作对象。当Session...
描述部分提到的“博文链接:https://zhaoshijie.iteye.com/blog/998230”虽然没有提供具体信息,但可以推断出这是一个完整的博客文章,其中详细阐述了关于Hibernate二级缓存的使用和实现,可能包括了配置、注解的...
本篇将深入探讨Hibernate的一级缓存和二级缓存,以及查询缓存的配置和使用。 ### 一级缓存 一级缓存是Hibernate默认提供的缓存,它是Session级别的,每个Hibernate Session都有一个私有的、本地的一级缓存。当我们...