在休眠中按 id 高效加载多个实体

因此,我通过id获取了特定实体的许多实例:

for(Integer songId:songGroup.getSongIds()) {
   session = HibernateUtil.getSession();
   Song song = (Song) session.get(Song.class,id);
   processSong(song);
}

这将为每个 ID 生成一个 SQL 查询,因此我突然想到我应该在一个 ID 中执行此操作,但是除了运行查询之外,我找不到在一次调用中获取多个实体的方法。所以我写了一个查询

return (List) session.createCriteria(Song.class)
       .add(Restrictions.in("id",ids)).list();

但是,如果我启用第二级缓存,这并不意味着我的旧方法能够从第二级缓存返回对象(如果之前已请求过它们),但我的查询将始终转到数据库。

正确的方法是什么?


答案 1

你在这里要求做的是让Hibernate为你的标准做特殊情况处理,这有点需要问。

你必须自己动手,但这并不难。使用 ,您可以获取对缓存对象的实际存储的引用。执行类似操作:SessionFactory.getCache()

for (Long id : allRequiredIds) {
  if (!sessionFactory.getCache().containsEntity(Song.class, id)) {
    idsToQueryDatabaseFor.add(id)
  } else {
    songs.add(session.get(Song.class, id));
  }
}

List<Song> fetchedSongs = session.createCriteria(Song.class).add(Restrictions.in("id",idsToQueryDatabaseFor).list();
songs.addAll(fetchedSongs);

然后从那里检索缓存中的歌曲,并且未使用单个提取的歌曲。select


答案 2

如果您知道 ID 存在,则可以使用 load(..) 创建代理,而无需实际命中 DB:

返回具有给定标识符的给定实体类的持久实例,获取指定的锁定模式(假定该实例存在)。

List<Song> list = new ArrayList<>(ids.size());
for (Integer id : ids)
  list.add(session.load(Song.class, id, LockOptions.NONE));

访问非标识符访问器后,Hibernate 将检查缓存并在需要时回退到 DB,如果已配置,则使用批处理提取。

如果 ID 不存在,则在加载对象后将发生 ObjectNotFoundException。这可能是您在代码中的某个地方,您不会真正期望异常 - 您最终使用的是一个简单的访问器。因此,要么100%确定ID存在,要么至少在您期望的早期强制使用ObjectNotFoundException,例如在填充列表之后。


推荐