使用 Hibernate 的 @NaturalId 查找对象有什么好处吗?
我担心 Hibernate 执行两个查询以使用 @NaturalId 获取对象。第一个查询只是为了获取 id,第二个查询是为了加载真实的对象。
使用 Hibernate 的 @NaturalId 查找对象有什么好处吗?
我担心 Hibernate 执行两个查询以使用 @NaturalId 获取对象。第一个查询只是为了获取 id,第二个查询是为了加载真实的对象。
主要优点是您可以使用缓存来解析实体而无需访问数据库。
当ResolveNaturalIdEvent
事件被抛出时,Hibernate 将尝试:
从一级缓存加载关联的实体 ID
从二级缓存加载关联的实体 ID(如果启用)
如果一级缓存不能满足我们的请求,则回退到数据库查询
Serializable entityId = resolveFromCache( event );
if ( entityId != null ) {
if ( traceEnabled )
LOG.tracev( "Resolved object in cache: {0}",
MessageHelper.infoString( persister, event.getNaturalIdValues(), event.getSession().getFactory() ) );
return entityId;
}
return loadFromDatasource( event );
因此,这与使用通过持久性上下文 API 加载的实体(例如EntityManager.find())具有相同的好处。
执行两个查询的唯一时间是实体尚未缓存(一级或二级缓存)。
至少一个优点是您将从一级缓存中受益。因此,例如,如果您通过电子邮件(即 naturalid)加载用户,您将只从 db 中获取主键 id,如果用户对象已经存在,则从一级缓存中获取用户对象。加载时间更快,网络数据传输更少。
使用@NaturalID 还有另一个好处,它与查询缓存的使用有关(我在 Hibernate 4.3.8 中基于此答案)如果您配置自然 ID,然后通过 NaturalID Restrictions#naturalId或Session查询实体#byNaturalId即使表已被修改,您也可能会遇到查询缓存。
Hibernate 保留表的更新时间戳的缓存,以说明查询缓存条目是否为upToDate。
UpdateTimestampsCache:跟踪特定表的最新更新的时间戳。将底层缓存实现的缓存超时设置为高于任何查询缓存的超时是很重要的。事实上,我们建议根本不要将底层缓存配置为过期。请特别注意,LRU 缓存过期策略永远不合适。
简单来说,如果 Hibernate 缓存了以下内容
*---------------------------------------------------------- *
| Query Cache |
|---------------------------------------------------------- |
| ["from Team where trophies = ", ["10"] ] -> [1,2] ] |
*---------------------------------------------------------- *
如果具有 ID 的团队将赢得新奖杯,您会希望查询“from Team where trophies=10”将不再返回它。为了实现这一点,Hibernate 会记录表的最后更新时间,然后如果查询缓存条目早于这个时间戳,它就不相信它的结果。
我说它不信任,因为结果仍然有效,但 Hibernate 不知道,因为缓存不是每个实体,原因很明显。因此,例如,如果 ID=3 的团队将被更新,则 1,2 的条目将无效,即使这些团队没有被更新。更重要的是,如果 Team 1 升级但他的奖杯数与以前一样,查询缓存条目也会失效,尽管它仍然有效。
如果我们通过 NaturalID 查询
List results = s.createCriteria( Citizen.class )
.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", ste ) )
.list()
我们将允许 Hibernate 信任查询缓存条目,即使它们不是 upToDate。这就像说:“是的 Hibernate,我知道该表已更新,但我向您保证这些实体的 NaturalID 没有更改,因此您可以信任与 NaturalID 查询对应的条目”。
Hibernate 必须检查一些事情以查看查询是否可以避免 upToDate 检查。这可以在Loader#getResultFromQueryCache中看到
boolean isImmutableNaturalKeyLookup =
queryParameters.isNaturalKeyLookup() && // If you are querying by NaturalID
resultTypes.length == 1 &&
resultTypes[0].isEntityType() && // and you are returning an Entity (no projections)
getEntityPersister( EntityType.class.cast( resultTypes[0] ) )
.getEntityMetamodel()
.hasImmutableNaturalId(); // and that Entity has an immutable NaturalID (Is the default and I can't imagine when we could use a mutable NaturalID)
标志 isImmutableNaturalKeyLookup 被传递给Query#get。让我们看看StandardQueryCache是如何使用它的。
final List cacheable = getCachedResults( key, session );
final Long timestamp = (Long) cacheable.get( 0 );
if ( !isNaturalKeyLookup && !isUpToDate( spaces, timestamp, session ) ) {
if ( DEBUGGING ) {
LOG.debug( "Cached query results were not up-to-date" );
}
return null;
}
return cacheable; // more or less
如果 isNaturalKeyLookup 为真,则不检查 isUpToDate。
Hibernate: Cache Queries the Natural Id Way是一篇关于查询缓存和 NaturalID 的古老但很棒的文章,有助于理解。