Impacto na NHibernate cache para pesquisas com resultados incluindo valor calculado mapeada como uma fórmula (e.g. classificação)

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

Pergunta

Ao definir uma propriedade calculada usando uma fórmula em NHibernate, quais são as implicações para quando a fórmula varia seu resultado dependendo das restrições de consulta, especialmente com relação ao cache de consulta?

Mais especificamente, considere o seguinte classe C # simples:

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

mapeada com o seguinte mapeamento simples 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>

Correndo com uma fábrica de sessão com opção hibernate.cache.use_query_cache conjunto para true, e consultados nas seguintes formas:

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

Will NHibernate comportar de uma maneira razoável para as Entidades devolvidos? Ou poderia o valor "Rank" de um cache de consulta poluir o valor de outra consulta Posto devido ao cache de consultas? Existem outras preocupações ao usar tal fórmula em mapeamentos NHibernate?

EDIT:

Também pode ser interessante notar que no meu caso particular, "Entidade" não é uma entidade de negócios de primeira classe, mas uma espécie de meta-entidade. Ele mapeia a uma visão do banco de dados indexada de outras entidades de primeira classe e é usado exclusivamente para a pesquisa (o (id) chamada Session.load é planejado e nunca deve realmente acontecer na prática).

E, se houver são implicações para caching, como eu suspeito, que alternativas podem existir para um caso de uso semelhante, para evitar potenciais problemas?

Foi útil?

Solução

Depois de mais experimentação: Sim, existem implicações de cache que podem resultar em resultados inconsistentes; NHibernate não pode saber automaticamente que a fórmula poderia mudar valores entre consultas para resultados entidade com o mesmo identificador (e assume que não vai).

Ter um mapeamento de classe como aqueles em questão resultaria no ranking a ser armazenado com o resto dos dados entidade. Isto torna possível que uma consulta posterior vai acabar retornando um valor de classificação de alguma outra consulta, em vez da consulta ser executado e, portanto, têm fileiras que não são sequenciais como esperado.

NHibernate tem separado consulta e caches entidade (há são na verdade dois caches entidade - o sessão cache eo < a href = "http://nhforge.org/doc/nh/en/index.html#performance-cache" rel = "nofollow noreferrer"> segunda entidade de nível de cache ) e os impactos dependem de quais são sendo usado.

Quando o cache de consulta não está habilitado, valores de classificação incorreta pode ser recebida se você fizer duas consultas diferentes dentro da mesma sessão que compartilham um resultado, mas com diferentes fileiras. Neste caso, a segunda consulta da mesma sessão não irá substituir os dados da entidade já na sessão da primeira consulta (uma vez que poderia ter mudado para essa unidade de trabalho), de modo que o valor de classificação retornado será o mesmo da primeira consulta, em vez da classificação real da segunda consulta. Expulsar os resultados da primeira consulta deve evitar esse problema (mas não é a solução recomendada; veja abaixo)

Quando o cache de consultas é ativado, valores de classificação incorreta também pode ser recebido quando repetir a mesma consulta depois de alguma outra consulta executou que tiveram um resultado com um grau diferente. Neste caso, a primeira execução da consulta adiciona os identificadores resultado para o cache de consultas e as entidades (com a sua classificação) para o cache entidade. A consulta intercalada (quando executado em outra sessão) pode resultar em uma mudança no valor de classificação armazenado com a entidade no cache entidade. Quando a primeira consulta é executada-re, os identificadores em cache são usados ??para pesquisar as entidades em cache (com as fileiras alterado).


O problema pode ser resolvido completamente, alterando a entidade a incluir apenas os valores persistiu para a entidade (ou seja, excluindo a classificação). Então, pela consulta, use uma projeção para extrair o identificador e o posto para essa consulta:

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

Neste caso, uma vez que a classificação é um tipo de valor, o cache de consultas irá armazenar o posto ao lado dos identificadores de resultados da consulta para que específica consulta. Em seguida, usando uma segunda consulta, pesquisar os valores de entidade usando os identificadores projetadas. A parte complicada é que você vai querer evitar um problema tipo N+1 ao realizar a consulta entidade e você terá de criar outra estrutura de dados para se casar com o Entity e sua classificação associado para essa consulta.

É um pouco irritante que você tem que usar duas consultas separadas em vez de uma única consulta, mas esta parece ser a única maneira de usar os caches de forma adequada.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top