Влияние на кэширование NHibernate для поисков с результатами, включая вычисленное значение, отображаемое в виде формулы (например, рейтинга)

StackOverflow https://stackoverflow.com/questions/1627707

Вопрос

При определении вычисляемого свойства с помощью формулы в NHibernate, каковы последствия того, когда формула изменяет свой результат в зависимости от ограничений запроса, особенно в отношении кэширования запроса?

В частности, рассмотрим следующий простой класс C #:

public class Entity
{
    public Entity() { }
    public virtual int Id { get; protected set; }
    public virtual string Key { get; protected set; }
    public virtual string Value { get; protected set; }
    public virtual int Rank { get; protected set; }
}

Сопоставлено со следующим простым сопоставлением NHibernate:

<class name="Entity" mutable="false">
    <id name="Id">
        <generator class="native">
    </id>
    <property name="Key"/>
    <property name="Value"/>
    <property name="Rank" formula="row_number() over(order by value)">
</class>

Запуск с фабрикой сеансов с параметром hibernate.cache.use_query_cache , для которого установлено значение true , и запрашивается следующими способами:

ICriteria criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
IList<Entity> queryResult1 = criteria.List<Entity>();

criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
criteria.Add(Restrictions.Like("Key", "a", MatchMode.Anywhere));
IList<Entity> queryResult2 = criteria.List<Entity>();

Entity directResult = session.Load<Entity>(id);

Будет ли NHibernate вести себя разумно для возвращенных Сущностей? Или может быть "Ранг" значение из одного кэшированного запроса загрязняет значение Rank другого запроса из-за кеша запросов? Есть ли другие проблемы при использовании такой формулы в отображениях NHibernate?

РЕДАКТИРОВАТЬ:

Также стоит отметить, что в моем конкретном случае " Entity " это не первоклассный бизнес-объект, а своего рода мета-объект. Он сопоставляется с индексированным представлением базы данных поверх других объектов первого класса и используется исключительно для поиска (вызов session.Load (id) надуман и никогда не должен происходить на практике).

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

Это было полезно?

Решение

После дальнейших экспериментов: да, есть последствия для кэша, которые могут привести к противоречивым результатам; NHibernate не может автоматически знать, что формула может изменить значения между запросами для результатов сущностей с одним и тем же идентификатором (и предполагает, что это не так).

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

NHibernate имеет отдельный запрос и кэши сущностей (там на самом деле это два кеша сущностей - кеш сессии и < a href = "http://nhforge.org/doc/nh/en/index.html#performance-cache" rel = "nofollow noreferrer"> кэш сущностей второго уровня ), а влияние зависит от того, какие из них использовался.

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

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

<Ч>

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

ICriteria criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
criteria.SetProjection
    (Projections.Id(), 
     Projections.SqlProjection("row_number() over(order by value) as Rank",
                               new[] { "Rank" },
                               new[] { NHibernateUtil.Int32 }));

В этом случае, поскольку ранг является типом значения, кэш запроса будет хранить ранг рядом с идентификаторами результата запроса для этого определенного запроса. Затем, используя второй запрос, ищите значения сущностей, используя прогнозируемые идентификаторы. Сложность состоит в том, что вы захотите избежать проблемы с типом N + 1 при выполнении запроса сущности, и вам нужно будет создать другую структуру данных, чтобы объединить Entity и связанный с ним ранг для этого запроса.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top