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