NVarchar e NHibernate

Pode não ser novidade, mas as vezes podemos cair em algumas armadilhas que se não ficarmos atentos, podemos ter problemas de performance relacionados à campos do tipo de dado string. Apesar de focar o artigo no NHibernate, não é um problema somente dele, na verdade, acaba sendo mais um problema do banco de dados, pois ele pode acontecer independente se estiver ou não utilizando algum ORM na aplicação. Considere as consultas abaixo:

SELECT * FROM Duplicata WHERE PrefixoDoCnpj = N'111111111'
SELECT * FROM Duplicata WHERE PrefixoDoCnpj = '111111111'

O campo PrefixoDoCnpj é um campo do tipo varchar(11) na base de dados. Temos também um índice sobre este campo para otimizar as pesquisas por ele, afinal, é muito comum querer retornar todas as duplicatas de um determinado CNPJ. A única diferença que se nota em ambas as consultas é que a primeira tem o caractere “N” antes do valor do parâmetro. Isso serve para indicar ao SQL Server que o tipo de dado é nvarchar. Ao rodar as consultas, a primeira será muito mais lenta que a segunda versão, que utiliza o mesmo tipo de dado da coluna (varchar). Para entender melhor a diferença, vamos analisar os planos de execução:

nhnvarchar

Na primeira consulta, onde o parâmetro tem um tipo de dados diferente do tipo de dado da coluna, ele está fazendo um index scan, obrigando o SQL Server a tocar em cada uma das linhas do índice e fazer a conversão implícita de cada um dos valores (pode-se perceber isso na imagem acima), e sendo assim, o custo está diretamente associado à quantidade de linhas que a tabela tenha. Já a segunda consulta, o SQL Server opta por fazer uso do index seek, que recorre a forma de pesquisa onde ele extrai diretamente os registros que satisfazem o critério desejado.

Tudo o que vimos acima tem a ver exclusivamente com SQL Server. Se optarmos por utilizar o NHibernate para que ele faça a geração da base de dados a partir do nosso domínio, ao utilizar as configurações padrão, todos os campos strings serão mapeados como Unicode, ou seja, nvarchar. Além dos problemas de performance que vimos acima, por aceitar caracteres Unicode, precisamos de mais espaço de armazenamento, e dependendo do tipo de aplicação que está se desenvolvendo, isso não é necessário. E ainda, se criamos scripts manuais para a criação/manutenção de itens da base dados, optando pelo tipo de dado varchar e no NHibernate sermos omissos em relação à isso, o problema da degradação da performance acontecerá.

Para deixarmos explícito para ao NHibernate que ele deve utilizar varchar ao invés de nvarchar, podemos definir o tipo de dado da propriedade como AnsiString. Isso indicará ao SQL Server a tratar esta coluna da forma que desejamos (varchar), e além disso, podemos já definir o atributo sql-type para ser utilizado na geração do scripts de criação da base de dados se isso for necessário.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="AppDeExemplo" namespace="AppDeExemplo">
  <class name="Sacado">
    <id name="PrefixoDoCnpj" length="11" type="AnsiString">
      <column name="PrefixoDoCnpj" sql-type="varchar(11)" length="11" />
      <generator class="assigned" />
    </id>
    <!-- Demais Configurações -->
    <bag name="Duplicatas">
      <key column="PrefixoDoCnpj" />
      <composite-element class="Duplicata">
        <!-- Demais Configurações -->
      </composite-element>
    </bag>
  </class>
</hibernate-mapping>
Anúncios