【问题标题】:What's the advantage of finding an entity using the Hibernate @NaturalId使用 Hibernate @NaturalId 查找实体有什么好处
【发布时间】:2015-03-26 15:11:54
【问题描述】:

使用 Hibernate 的 @NaturalId 查找对象有什么好处吗?

我担心 Hibernate 执行两个查询以使用 @NaturalId 获取对象。 第一个查询只是为了获取 id,第二个查询是为了加载真实的对象。

【问题讨论】:

    标签: java hibernate caching orm natural-key


    【解决方案1】:

    主要优点是您可以使用缓存来解析实体而无需访问数据库。

    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())。

    执行两个查询的唯一时间是实体尚未缓存(一级或二级缓存)。

    【讨论】:

      【解决方案2】:

      至少一个优点是您将从一级缓存中受益。因此,例如,如果您通过电子邮件加载用户(这是 naturalid),您将只从 db 中获取主键 id,如果用户对象已经存在,则从一级缓存中获取用户对象。加载时间更快,网络数据传输更少。

      【讨论】:

        【解决方案3】:

        使用@NaturalID 还有另一个好处,它与查询缓存的使用有关(我在 Hibernate 4.3.8 中给出了这个答案) 如果您配置 natural ID,然后通过 NaturalID Restrictions#naturalIdSession#byNaturalId 查询实体,即使表已被修改,您也可能会遇到查询缓存。

        Hibernate 会保留一个 cache of update timestamps 的表来判断查询缓存条目是否为 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 的古老但很棒的帖子,有助于理解。

        【讨论】:

          猜你喜欢
          • 2010-11-26
          • 2014-09-02
          • 1970-01-01
          • 2015-05-11
          • 1970-01-01
          • 2013-11-12
          • 1970-01-01
          • 1970-01-01
          • 2016-03-31
          相关资源
          最近更新 更多