看起来很简单的问题。我尝试了很多理解但失败了。
据我所知,休眠一级缓存是指会话级缓存。当我们在同一个会话中多次检索同一个对象时,它将从缓存中检索。
例如,我在数据库中有一个 id 为 100 的员工记录。
我打开了一个会话并获取了该员工对象。直到我关闭会话该对象在同一会话中可用。
问题:为什么我需要在同一个会话中多次检索同一个对象(在我关闭它之前它在会话中如何可用)?
看起来很简单的问题。我尝试了很多理解但失败了。
据我所知,休眠一级缓存是指会话级缓存。当我们在同一个会话中多次检索同一个对象时,它将从缓存中检索。
例如,我在数据库中有一个 id 为 100 的员工记录。
我打开了一个会话并获取了该员工对象。直到我关闭会话该对象在同一会话中可用。
问题:为什么我需要在同一个会话中多次检索同一个对象(在我关闭它之前它在会话中如何可用)?
据我所知,休眠一级缓存是指会话级缓存。当我们在同一个会话中多次检索同一个对象时,它将从缓存中检索。
说对了一半。除了你说的,一级缓存的一个主要原因是,在同一个会话下,Hibernate 将确保同一个实体(具有相同 ID 的实体)将由同一个对象实例表示。
仅当您通过 ID 从会话中获取实体时,您所说的才是正确的:
Foo foo1 = session.get(Foo.class, 1L);
Foo foo2 = session.get(Foo.class, 1L);
第一次调用get()
将去 DB 加载Foo
。当调用第二个调用时,Hibernate 将检查在此会话中Foo
是否已经检索到 ID 为 1 的任何对象。正如之前检索到的那样,Hibernate 将简单地获取该Foo
实例并返回给您。
但是,这种情况并不是您会看到一级缓存生效的最常见情况。考虑一下:
// psuedo code only
User user = findByUserName("ADRIAN"); // assume ID = 777
List<User> users = findAllActiveUsers();
(假设上述查找器通过 Hibernate 会话在内部运行查询)当 Hibernate 运行第二个查询时,Hibernate 在内部运行 SQL,获取结果集,并将每条记录转换为用户。假设其中一个活动用户的 ID 为 777。当 Hibernate 构造该 User 对象实例时,它将首先检查它是否存在于一级缓存中。因为它是以前检索的(在以前的查询中通过用户名查找),而不是构造一个新的 User 对象实例,Hibernate 将简单地重用以前构造的实例(并存储在一级缓存中)并在结果列表中使用它。
通过这样做,Hibernate 可以确保,在同一个会话中,如果您通过不同的方式检索相同的实体(具有相同 ID 的相同类),您始终可以假设该实体将是相同的对象实例。
想一个更复杂的例子,你试图Order
从你的系统中检索 s,它指的是User
(假设多对一)。您会发现,不同的是Order
,每当它引用相同User
的(在数据库中)时,它实际上是指相同的User
对象实例。
对于问题
在我关闭它之前它如何在会话中可用
它更像是 Hibernate 的内部实现细节。但是,从概念上,你可以想象成,每个 Session 内部都有一个 Map,key 是 Entity Type + ID,value 是实体对象实例。
当您从数据库查询并且会话为您构建实体时,对于每个实体,如果它已经存在,它将从地图中查找。如果没有,会话将构建实体并放入地图。如果它已经存在,会话将简单地使用地图中的实体
当您通过 ID(通过等)获取实体时,类似Session.get()
的Session.load()
想法
您不需要从EntityManager 多次检索相同的对象,但如果这样做,您将获得相同的对象。这就是缓存的意义所在。至于您的第二个问题: EntityManager 保留对该对象的引用,如果您再次请求相同的对象,则返回它。
我建议您阅读 JPA 教程,例如http://docs.oracle.com/javaee/6/tutorial/doc/bnbpz.html。恕我直言,您应该专注于学习 JPA,并将 Hibernate 简单地视为 JPA 提供者。
如果有后续调用需要检索此员工对象,则可以从同一会话中获取,而不是从数据库中获取
这取决于您如何管理Session
或EntityManager
。如果它是按请求创建的,则无需再次查询。但是如果Session
/EntityManager
被重用,那么同一个对象可以被多次检索,因此它将从一级缓存中返回。