使用Hibernate的@NaturalId找到对象有什么好处吗?
我很担心Hibernate使用@NaturalId执行两个查询来获取对象。 第一个查询只是为了获取id而第二个查询是为了加载真实对象。
答案 0 :(得分:6)
正如我在this article中所解释的那样,主要优点是您可以使用缓存来解析实体而无需访问数据库。
当抛出ResolveNaturalIdEvent事件时,Hibernate会尝试:
如果第一级缓存无法满足我们的请求,则回退到数据库查询
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())的好处相同。
执行两个查询的唯一时间是实体尚未缓存(第一级或第二级缓存)。
答案 1 :(得分:2)
至少有一个优点是您将受益于第一级缓存。因此,例如,如果您通过电子邮件加载用户(这是自然文件),您将只从db获取主键ID,如果已经存在,则从第一级缓存获取用户对象。因此,加载时间更短,因为网络数据传输更少。
答案 2 :(得分:0)
使用@NaturalID还有另一个好处,并且与查询缓存的使用有关(我在Hibernate 4.3.8中基于这个答案) 如果您配置natural ID,然后按NaturalID Restrictions#naturalId或Session#byNaturalId查询实体,即使表已被修改,您也可能会遇到查询缓存。
如果查询缓存条目为cache of update timestamps,Hibernate会保留upToDate个表。
UpdateTimestampsCache :跟踪特定表的最新更新的时间戳。重要的是将底层高速缓存实现的高速缓存超时设置为高于任何查询高速缓存的超时的值。实际上,我们建议不要将底层缓存配置为到期。特别要注意,LRU缓存到期策略永远不合适。
简单来说,如果Hibernate缓存了以下内容
*---------------------------------------------------------- *
| Query Cache |
|---------------------------------------------------------- |
| ["from Team where trophies = ", ["10"] ] -> [1,2] ] |
*---------------------------------------------------------- *
如果带有ID的团队赢得新奖杯,您将需要“来自奖杯= 10的团队”的查询将不再返回。为了实现这一点,Hibernate会记录上次更新表的时间,然后如果查询缓存条目早于此时间戳,则它不信任它的结果。
我说它不信任,因为结果仍然有效,但Hibernate不会知道,因为缓存不是每个实体,原因很明显。因此,例如,如果ID = 3的团队将被提升,则即使这些团队没有被提升,1,2的条目也将失效。 更重要的是,如果团队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为true,则不检查isUpToDate。
Hibernate: Cache Queries the Natural Id Way是关于查询缓存和NaturalID的一篇古老而精彩的文章,有助于理解。