Влияние на кэширование NHibernate для поисков с результатами, включая вычисленное значение, отображаемое в виде формулы (например, рейтинга)
-
06-07-2019 - |
Вопрос
При определении вычисляемого свойства с помощью формулы в 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
и связанный с ним ранг для этого запроса.
Немного раздражает, что вам приходится использовать два отдельных запроса, а не один запрос, но, похоже, это единственный способ использовать кэши соответствующим образом.