Преимущество поиска объекта с помощью @NaturalId Hibernate

Есть ли какое-то преимущество, чтобы найти объект, используя @NaturalId Hibernate?

Меня беспокоит тот факт, что Hibernate выполняет два запроса для получения объекта, используя @NaturalId. Первый запрос просто для получения идентификатора, а второй запрос для загрузки реального объекта.

3 ответа

Решение

Как я объяснил в этой статье, главное преимущество заключается в том, что вы можете использовать кэш для разрешения сущности, не обращаясь к базе данных.

Когда генерируется событие ResolveNaturalIdEvent, Hibernate попытается:

  • загрузить идентификатор связанной сущности из кэша 1-го уровня
  • загрузить связанный идентификатор объекта из кэша 2-го уровня (если он включен)
  • откат к запросу базы данных, если кэш 1-го уровня не может удовлетворить наш запрос

    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- интерфейс Persistence Context (например, EntityManager.find ()).

Единственный раз, когда выполняются два запроса, это когда объект еще не кэширован (кэш 1-го или 2-го уровня).

По крайней мере, одно преимущество - вы получите выгоду от кэша первого уровня. Так, например, если вы загрузите пользователя по электронной почте (что является натуральным), вы получите только первичный ключ с идентификатором из db и пользовательский объект из кэша первого уровня, если он уже существует. Так быстрее время загрузки и меньше передачи данных по сети.

Еще одно преимущество использования @NaturalID связано с использованием кеша запросов (я основываю этот ответ в Hibernate 4.3.8). Если вы настраиваете естественный идентификатор, а затем запрашиваете сущность с помощью NaturalID Restrictions # naturalId или Session#byNaturalId Вы можете использовать кеш запросов, даже если таблица была изменена.

Hibernate хранит кэш временных меток обновления таблиц, чтобы указать, является ли запись в кэше запросов upToDate.

UpdateTimestampsCache: отслеживает метки времени последних обновлений определенных таблиц. Важно, чтобы время ожидания кеша базовой реализации кеша было установлено на более высокое значение, чем время ожидания любого из кешей запросов. Фактически, мы рекомендуем, чтобы базовый кеш вообще не был настроен на истечение срока действия. В частности, обратите внимание, что политика истечения срока действия LRU-кэша никогда не подходит.

На простом английском языке, если Hibernate кэшировал следующее

*----------------------------------------------------------   *
|                       Query Cache                           |                     
|----------------------------------------------------------   |
| ["from Team where trophies = ", ["10"] ] -> [1,2] ]           |
*----------------------------------------------------------   *

Вы захотите, чтобы, если команда с идентификатором выиграла новый трофей, запрос "от команды, где трофеи =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: Запросы кеша Natural Id Way - это старый, но отличный пост о кеше запросов и NaturalID, который поможет понять.

Другие вопросы по тегам