在 Hibernate 中通过 id 高效加载多个实体
因此,我通过 id 获取特定实体的多个实例:
for(Integer songId:songGroup.getSongIds()) {
session = HibernateUtil.getSession();
Song song = (Song) session.get(Song.class,id);
processSong(song);
}
这会为每个 id 生成一个 SQL 查询,所以我想到我应该在一个中执行此操作,但我找不到一种方法来获取多个一次调用中的实体(运行查询除外)。所以我编写了一个查询
return (List) session.createCriteria(Song.class)
.add(Restrictions.in("id",ids)).list();
但是,如果我启用二级缓存并不意味着我的旧方法能够从二级缓存返回对象(如果之前已请求过它们),但我的查询将始终转到数据库。
这样做的正确方法是什么?
So, I'm getting a number of instances of a particular entity by id:
for(Integer songId:songGroup.getSongIds()) {
session = HibernateUtil.getSession();
Song song = (Song) session.get(Song.class,id);
processSong(song);
}
This generates a SQL query for each id, so it occurred to me that I should do this in one, but I couldn't find a way to get multiple entities in one call except by running a query. So I wrote a query
return (List) session.createCriteria(Song.class)
.add(Restrictions.in("id",ids)).list();
But, if I enable 2nd level caching doesn't that mean that my old method would be able to return the objects from the 2nd level cache (if they had been requested before) but my query would always go to the database.
What the correct way to do this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您在这里要求做的是让 Hibernate 为您的 Criteria 进行特殊情况处理,这是一个很多要求。
你必须自己做,但这并不难。使用
SessionFactory.getCache()
,您可以获得对缓存对象的实际存储的引用。执行如下操作:然后从缓存中检索歌曲,而未通过单个
选择
提取的歌曲。What you're asking to do here is for Hibernate to do special case handling for your Criteria, which is kind of a lot to ask.
You'll have to do it yourself, but it's not hard. Using
SessionFactory.getCache()
, you can get a reference to the actual storage for cached objects. Do something like the following:Then the Songs from the cache get retrieved from there, and the ones that are not get pulled with a single
select
.如果您知道 ID 存在,则可以使用 load(..) 创建代理而不实际访问数据库:
一旦访问非标识符访问器,Hibernate 将检查缓存并在需要时回退到数据库,如果配置,则使用批量获取。
如果 ID 不存在,则加载对象后将出现 ObjectNotFoundException。这可能是您的代码中您不会真正期望出现异常的地方 - 您最终使用了一个简单的访问器。因此,要么 100% 确定 ID 存在,要么至少在您期望的位置尽早强制抛出 ObjectNotFoundException,例如在填充列表后立即执行。
If you know that the IDs exist, you can use load(..) to create a proxy without actually hitting the DB:
Once you access a non-identifier accessor, Hibernate will check the caches and fallback to DB if needed, using batch-fetching if configured.
If the ID doesn't exists, a ObjectNotFoundException will occur once the object is loaded. This might be somewhere in your code where you wouldn't really expect an exception - you're using a simple accessor in the end. So either be 100% sure the ID exists or at least force a ObjectNotFoundException early where you'd expect it, e.g. right after populating the list.
Hibernate 二级缓存与 Hibernate 查询缓存之间存在差异。
以下链接对此进行了很好的解释: http://www.javalobby.org/java/forums /t48846.html
简而言之,
如果您多次使用相同的查询和相同的参数,那么您可以结合使用两者来减少数据库命中率。
There is a difference between hibernate 2nd level cache to hibernate query cache.
The following link explains it really well: http://www.javalobby.org/java/forums/t48846.html
In a nutshell,
If you are using the same query many times with the same parameters then you can reduce database hits using a combination of both.
您可以做的另一件事是对 id 列表进行排序,并识别连续 id 的子序列,然后在单个查询中查询每个子序列。例如,给定
Listids
,执行以下操作(假设您有 Java 中的 Pair 类):Another thing that you could do is to sort the list of ids, and identify subsequences of consecutive ids and then query each of those subsequences in a single query. For example, given
List<Long> ids
, do the following (assuming that you have a Pair class in Java):在循环中逐一获取每个实体可能会导致 N+1个查询问题。
因此,一次获取所有实体并随后进行处理会更有效。
现在,在您提出的解决方案中,您使用的是旧版 Hibernate Criteria,但由于它自 Hibernate 4 起已被弃用,并且可能会在 Hibernate 6 中删除,因此最好使用以下替代方案之一。
JPQL
您可以使用如下所示的 JPQL 查询:
Criteria API
如果您想动态构建查询,则可以使用 Criteria API 查询:
Hibernate 特定的 multiLoad
现在,JPQL 和 Criteria API 可以从
hibernate.query.in_clause_parameter_padding
优化,它允许您增加SQL语句缓存机制。有关通过标识符加载多个实体的更多详细信息,请查看本文。
Fetching each entity one by one in a loop can lead to N+1 query issues.
Therefore, it's much more efficient to fetch all entities at once and do the processing afterward.
Now, in your proposed solution, you were using the legacy Hibernate Criteria, but since it's been deprecated since Hibernate 4 and will probably be removed in Hibernate 6, so it's better to use one of the following alternatives.
JPQL
You can use a JPQL query like the following one:
Criteria API
If you want to build the query dynamically, then you can use a Criteria API query:
Hibernate-specific multiLoad
Now, the JPQL and Criteria API can benefit from the
hibernate.query.in_clause_parameter_padding
optimization as well, which allows you to increase the SQL statement caching mechanism.For more details about loading multiple entities by their identifier, check out this article.