Impact sur la caching de NHibernate pour les searchs avec résultats incluant la valeur calculée mappée en tant que formule (par exemple, classment)

Lorsque vous définissez une propriété calculée à l'aide d'une formule dans NHibernate, quelles sont les implications pour quand la formule varie son résultat en fonction des ressortingctions de requête, en particulier en ce qui concerne la caching des requêtes?

Plus précisément, considérons la class C # simple suivante:

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

Mappé avec le mappage NHibernate simple suivant:

 <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> 

Exécution avec une fabrique de sessions avec l'option hibernate.cache.use_query_cache définie sur true et interrogée des manières suivantes:

 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(Ressortingctions.Like("Key", "a", MatchMode.Anywhere)); IList<Entity> queryResult2 = criteria.List<Entity>(); Entity directResult = session.Load<Entity>(id); 

NHibernate se comportera-t-il de manière raisonnable pour les Entités returnnées? Ou la valeur "Rank" d'une requête caching pourrait-elle polluer la valeur Rank d'une autre requête en raison du cache de requête? Existe-t-il d'autres problèmes lors de l'utilisation d'une telle formule dans les mappages de NHibernate?

MODIFIER:

Il peut également être intéressant de noter que dans mon cas particulier, «Entité» n'est pas une entité commerciale de premier ordre, mais une sorte de méta-entité. Il est mappé à une vue de database indexée sur d'autres entités de première class et est utilisé exclusivement pour la search (l'appel session.Load (id) est artificiel et ne devrait jamais réellement se produire dans la pratique).

Et, s'il y a des implications pour la caching, comme je le soupçonne, quelles alternatives pourraient exister pour un cas similaire afin d'éviter des problèmes potentiels?

Après d'autres expérimentations: Oui, il y a des implications dans le cache qui pourraient entraîner des résultats incohérents; NHibernate ne peut pas automatiquement savoir que la formule pourrait changer les valeurs entre les requêtes pour les résultats d'entité avec le même identificateur (et suppose que ce ne sera pas le cas).

Avoir un mappage de class comme ceux de la question entraînerait le stockage du rang avec le rest des données d'entité. Cela rend possible qu'une requête ultérieure finira par renvoyer une valeur de classment à partir d'une autre requête plutôt que la requête en cours d'exécution et donc avoir des rangs qui ne sont pas séquentiels comme prévu.

NHibernate a des caches de requête et d'entité séparés (il y a en fait deux caches d'entité – le cache de session et le cache d'entité de second niveau ) et les impacts dépendent de ceux qui sont utilisés.

Lorsque le cache de requête n'est pas activé, des valeurs de classment incorrectes peuvent être reçues si vous effectuez deux requêtes différentes dans la même session qui partagent un résultat mais avec des rangs différents. Dans ce cas, la deuxième requête de la même session ne replacea pas datatables d'entité déjà présentes dans la session à partir de la première requête (car elle pourrait avoir changé pour cette unité de travail), la valeur returnnée sera la même depuis la première requête plutôt que le rang réel de la deuxième requête. Evincer les résultats de la première requête devrait éviter ce problème (mais n'est pas la solution recommandée, voir ci-dessous)

Lorsque le cache de requête est activé, des valeurs de rang incorrectes peuvent également être reçues lors de la répétition de la même requête après l'exécution d'une autre requête ayant un résultat de rang différent. Dans ce cas, la première exécution de la requête ajoute les identificateurs de résultat au cache de requête et aux entités (avec leur rang) dans le cache d'entité. La requête entrelacée (lorsqu'elle est exécutée dans une autre session) peut entraîner une modification de la valeur de rang stockée avec l'entité dans le cache d'entité. Lorsque la première requête est ré-exécutée, les identifiants mis en cache sont utilisés pour searchr les entités mises en cache (avec les rangs modifiés).


Le problème peut être entièrement résolu en changeant l'entité pour inclure uniquement les valeurs persistantes pour l'entité (c'est-à-dire en excluant le rang). Ensuite, pour la requête, utilisez une projection pour extraire l'identifiant et le rang de cette requête:

 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 })); 

Dans ce cas, étant donné que le rang est un type de valeur, le cache de requête stocke le rang le long des identificateurs de résultat de requête pour cette requête spécifique . Ensuite, à l'aide d'une deuxième requête, searchz les valeurs d'entité à l'aide des identificateurs projetés. La partie délicate est que vous voudrez éviter un problème de type N+1 lors de l'exécution de la requête d'entité et vous devrez créer une autre structure de données pour marier l' Entity et son rang associé pour cette requête.

C'est un peu ennuyeux que vous ayez à utiliser deux requêtes distinctes plutôt qu'une seule requête, mais cela semble être la seule façon d'utiliser les caches d'une manière appropriée.