3

看起来很简单的问题。我尝试了很多理解但失败了。

据我所知,休眠一级缓存是指会话级缓存。当我们在同一个会话中多次检索同一个对象时,它将从缓存中检索。

例如,我在数据库中有一个 id 为 100 的员工记录。

我打开了一个会话并获取了该员工对象。直到我关闭会话该对象在同一会话中可用。

问题:为什么我需要在同一个会话中多次检索同一个对象(在我关闭它之前它在会话中如何可用)?

4

4 回答 4

6

据我所知,休眠一级缓存是指会话级缓存。当我们在同一个会话中多次检索同一个对象时,它将从缓存中检索。

说对了一半。除了你说的,一级缓存的一个主要原因是,在同一个会话下,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()想法

于 2016-09-14T08:48:20.320 回答
1

您不需要EntityManager 多次检索相同的对象,但如果这样做,您将获得相同的对象。这就是缓存的意义所在。至于您的第二个问题: EntityManager 保留对该对象的引用,如果您再次请求相同的对象,则返回它。

我建议您阅读 JPA 教程,例如http://docs.oracle.com/javaee/6/tutorial/doc/bnbpz.html。恕我直言,您应该专注于学习 JPA,并将 Hibernate 简单地视为 JPA 提供者。

于 2016-09-14T07:58:26.110 回答
1

如果有后续调用需要检索此员工对象,则可以从同一会话中获取,而不是从数据库中获取

于 2016-09-14T08:05:38.903 回答
1

这取决于您如何管理SessionEntityManager。如果它是按请求创建的,则无需再次查询。但是如果Session/EntityManager被重用,那么同一个对象可以被多次检索,因此它将从一级缓存中返回。

于 2016-09-14T08:12:41.263 回答