Integração entre Spring.NET e NHibernate

Publicado por Fabio A. Falavinha
28/7/2011
Categoria:
Tags: , , ,

O objetivo deste artigo é demonstrar a integração entre os frameworks Spring.NET e NHibernate, com ênfase na utilização do pattern DAO (Data Access Object) e suporte a transação.

Quando se utiliza o framework Spring.NET está explícito o uso do container IoC (Inversion of Control), pois todo conceito de configuração da arquitetura da aplicação está destinada a este modelo, que permite realizar o controle dos objetos e suas dependências de forma simples, através de anotações e configurações XML. O resultado de uso deste modelo é o pattern Dependency Injection (DI), que faz a integração dos objetos entre as camadas da aplicação.
O uso de NHibernate com Spring.NET não altera nenhum conceito dito acima, apenas reforça o modelo IoC para a configuração do framework com toda a integração necessária sobre a camada de persistência da aplicação, deixando como resultado ao desenvolvedor, a construção das classes DAOs que serão utilizadas, baseadas em sua modelagem.

Integrando Sprint.NET e NHibernate

Alguns pontos são importantes para a integração entre os dois frameworks:

Configuração NHibernate

A configuração do framework NHibernate é feita com a informação dos parâmetros de conexão com o banco de dados (para este exemplo vou utilizar Microsoft SQL Server 2005), e com a declaração do recurso ISessionFactory.

<objects xmlns="http://www.springframework.net"
         xmlns:db="http://www.springframework.net/database">

  <db:provider id="DbProvider"
                   provider="SqlServer-1.1"
                   connectionString="#{DefaultConnection.connectionString}"/>

  <object id="MySessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate">
    <property name="DbProvider" ref="DbProvider"/>
    <property name="MappingAssemblies">
      <list>
        <value>Persistence</value>
      </list>
    </property>
    <property name="HibernateProperties">
      <dictionary>

        <entry key="hibernate.connection.provider"
               value="NHibernate.Connection.DriverConnectionProvider"/>

        <entry key="hibernate.dialect"
               value="NHibernate.Dialect.MsSql2005Dialect"/>

        <entry key="hibernate.connection.driver_class"
               value="NHibernate.Driver.SqlClientDriver"/>

      </dictionary>
    </property>

  </object>

</objects>

Observem que os parâmetros de acesso ao banco de dados não estão declarados no contexto XML acima. Eles foram definidos no arquivo Web.config (ou App.config caso sua aplicação for WinForms ou Console).


<configuration>
<configSections>
<sectionGroup name="spring">
  <section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web" />
  <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
</sectionGroup>
</configSections>
<connectionStrings>
  <add name="DefaultConnection" connectionString="Server=localhost; Initial Catalog=BD; Integrated Security=SSPI"/>
  </connectionStrings>
</configuration>

Os arquivos de mapeamento estão definidos no assembly (projeto) Persistence, conforme a propriedade MappingAssemblies, do recurso LocalSessionFactoryObject.

Suporte a implementação DAO

O framework Spring.NET facilita a integração das classes DAOs com a injeção do recurso ISessionFactory, descrito no tópico acima.


public class ProductDAO : IProductDAO
{
    public ISessionFactory SessionFactory { get; set; }

    public void MakePersistence(Product product)
    {
        SessionFactory.GetCurrentSession().SaveOrUpdate(product);
    }
}

Para garantir completa integração entre Spring.NET e NHibernate, pode-se anotar a classe DAO com o atributo [Repository]. Este atribute garante suporte na tradução de exceções que serão lançadas ao executar consultas ao banco de dados através do hibernate.dialect (propriedade definida no recurso ISessionFactory).

[Repository]
public class ProductDAO : IProductDAO
{
    public ISessionFactory SessionFactory { get; set; }

    public void MakePersistence(Product product)
    {
        SessionFactory.GetCurrentSession().SaveOrUpdate(product);
    }
}
<objects>
  <!-- Exception translation object post processor -->
  <object type="Spring.Dao.Attributes.PersistenceExceptionTranslationPostProcessor, Spring.Data"/>
</objects>

Suporte a Transação

O framework NHibernate já disponibiliza o uso de transação através da API, porém a forma de uso com Spring.NET é via programação declarativa ou através da anotação de métodos de serviço.

<object id="transactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref="MySessionFactory"/>
</object>

Para utilizar o contexto de transação nos métodos das classes DAOs, anote os métodos das classes de serviço com o atributo [Transaction]. Habilite a configuração do framework Spring.NET junto a declaração dos serviços.

<objects xmlns="http://www.springframework.net"
	        xmlns:tx="http://www.springframework.net/schema/tx">

  <object id="productDAO" type="Persistence.Impl.ProductDAO, Persistence">
    <property name="sessionFactory" ref="MySessionFactory"/>
  </object>

  <object id="productService" type="Service.Impl.ProductService, Core">
    <property name="ProductDAO" ref="productDAO"/>
  </object>

  <tx:attribute-driven/>

</objects>
public class ProductService : IProductService
{
    public IProductDAO ProductDAO { get; set; }

    [Transaction(ReadOnly = false)]
    public void SaveProduct(Product product)
    {
         ProductDAO.MakePersistence(product);
    }
}

O atributo ReadOnly declarado na anotação Transaction informa que o estado do banco de dados será alterado. Qualquer erro lançado nesta operação, automaticamente será feito Rollback e a exceção será traduzida para a camada de serviço.

Outra forma de declarar uma transação é via arquivo XML, utilizando a forma declarativa de marcar os métodos que serão gerenciados pelo framework Spring.NET.

<object id="TxProxyConfigurationTemplate" abstract="true"
          type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data">

    <property name="PlatformTransactionManager" ref="HibernateTransactionManager"/>

    <property name="TransactionAttributes">
      <name-values>
        <!-- Add common methods across your services here -->
        <add key="Save*" value="PROPAGATION_REQUIRED"/>
      </name-values>
    </property>
  </object>

Vantagens: Spring.NET Transaction Management

OK! Realizar a configuração de um contexto de transação com Spring.NET e NHibernate não é uma tarefa difícil, vide código acima. Porém, se não é difícil configurar/utilizar este tipo de recurso, por que não podemos utilizar a própria API do .NET? Ou, por que não podemos utilizar um outro mecanismo para cumprir esta tarefa.

O uso de gerenciador de transação (Transaction Management) é visto no momento em que estamos modelando a arquitetura da aplicação, interagindo a camada de persistência (data access) com a camada de lógica de negócio (service business logic).

Quando uma determinada operação de business necessita acessar mais de um recurso DAO, uma transação deve ser aberta para garantir a integridade dos dados que serão manipulados naquele momento. Para fazer isso, podemos utilizar três APIs para prover tal funcionalidade:

  1. ADO.NET
  2. EnterpriseServices
  3. System.Transactions

A primeira abordagem seria utilizar as classes da API ADO.NET para garantir acesso comportilhado a uma única transação, e assim, a uma única conexão, entre todas as chamadas aos recursos DAOs, que o serviço estará executando. Um jeito simples, é fazer com que a camada de serviço crie a conexão e a transação e passe como argumento para as classes DAOs. Outro jeito, é compartilhar em uma ThreadLocal, a conexão e a transação, para que as classes DAOs possam ter acesso ao banco de dados.

A segunda abordagem é utilizar EnterpriseServices, que particulamente resolve o problema, porém força que sua transação seja SEMPRE distribuída (global), via Microsoft Distributed Transaction Coordinator (MS-DTC). Essa abordagem é menos eficiente do que utilizar a API ADO.NET (primeira abordagem).

A terceira abordagem é utilizar a API System.Transactions, o que oferece o uso de isolamento de escopos (TransactionScope) por recurso, garantindo que cada recurso possa estar em um determinado escopo. O problema desta abordagem é que múltiplos escopos de transação, ao serem detectados, são automaticamente promovidos para transações distribuídas (global), via Promotable Single Phase Enlistment (PSPE). Esta abordagem garante o uso compartilhado, porém menos eficiente do que o uso simples da API ADO.NET (primeira abordagem).

Portanto, qual seria uma boa abordagem para o uso de um gerenciador de transação, garantindo transparência, flexibilidade, eficiência e compartilhamento da transação entre recursos DAOs? A resposta está no uso do framework Spring.NET, que disponibiliza o uso de declarativo de transaçãoes, suportando a configuração dos escopos da transação (option metadata).

A abstração do conceito Transaction Management, para o framework Spring.NET, vai além do uso de transação de forma declarativa nos métodos de serviços. Inicia-se com a configuração de qual tipo de plataforma de gerenciamento de transação estará sendo utilizada na aplicação.

  1. ADO.NET – AdoPlatformTransactionManager
  2. EnterpriseServices – ServiceDomainPlatformTransactionManager
  3. System.Transactions (local/distribuídas) – TxScopePlatformTransactionManager
  4. NHibernate – HibernateTransactionManager

Utilizando-se toda esta abstração, caímos no uso de Proxy para o uso de transação com o framework Spring.NET (veja Figura 1).

Conclusão

O framework Spring.NET facilita o gerenciamento dos objetos que compõe as camadas de serviço e persistência, garantido suporte a transação, controle de exceção e simplicidade na configuração do framework NHibernate.

Mas o mesmo ainda não é verdade com o uso de Spring.NET com Entity Framework. A integração entre estes dois frameworks ainda não é realizada de forma transparente, referindo-se a interaçao entre o “DbContext” do EntityFramework e a camada de gerenciamento de transação do Spring.NET.

Acredito que o suporte de LINQ pelo framework NHibernate, torne-o ainda utilizável em aplicações mais simples, pois o uso extenso de Proxy nas entidades, modelo lazy, é muito questionável e não é um modelo agradável, muito menos eficiente.

Referências





Desenvolvido por hacklab/ com WordPress