Projektowanie, Programowanie, Codzienność – BeniaminZaborski.com

4 Marzec 2009

Ugryźć Spring.Net – (cz.4) Dostęp do danych – NHibernate

Filed under: Ugryźć Spring.Net — Tags: , , , , , , — Beniamin Zaborski @ 07:04

W tej części dalej zajmiemy się tematem dostępu do danych, ale nieco inną metodą. We współczesnych aplikacjach biznesowych stosowanie O/R Mappingu nie jest już niczym nadzwyczajnym, a wręcz stało się normą. Nie wypada zatem, aby Spring.NET nie wspierał takiego rozwiązania. Rzeczywiście Spring.NET posiada obsługę O/R Mappingu, a dokładniej mówiąc jest to NHibernate.
Na początek może dwa zdania co to takiego jest O/R Mapping. Jak podaje wikipedia jest to technika programistyczna do konwersji niekompatybilnych typów systemowych z postaci relacyjnej na obiektową. Jest to prawda, ponieważ relacyjne bazy danych operują na tabelach, obiektowe języki na obiektach. Istnieje wiele różnych komercyjnych jak i niekomercyjnych rozwiązań ORM, niezaprzeczalnie jednym z najbardziej popularnych jest NHibernate. NHibernate wiele zawdzięcza swojemu starszemu bratu z platformy Java o nazwie Hibernate – skądś to znamy ;).
Po takim krótkim wstępie chciałbym jednak oznajmić, iż celem artykułu nie jest opis samego NHibernate-a. Kieruje się tu do osób, które mają już pewne doświadczenie w pracy z NHibernate. Moim zadaniem tutaj będzie pokazanie jak Spring.NET integruje się z NHibernate.
Punktem wyjściowym będzie konfiguracja SessionFactory z użyciem kontenera Springa. Najlepiej przedstawi to poniższy przykład sekcji <objects>:

<db:provider id=”DbProvider”
    provider=”SqlServer-1.1″
    connectionString=
„Data Source=BENIAMINZ\SQLEXPRESS;Initial Catalog=ADMINET;Integrated Security=SSPI;”
/>
<object id=”MySessionFactory” type=”Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate”>
    <property name=”DbProvider” ref=”DbProvider”/>
    <property name=”MappingAssemblies”>
      <list>
        <value>BizDev.SampleSpring.Model</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>


Jak widać nasz SessionFactory bazuje na odpowiednim DbProvider o którym była mowa w poprzedniej części. DbProvider dostarcza nam tu informacji potrzebnych do nawiązania połączenia z bazą danych. W następnej kolejności określamy assembly w którym znajdują się pliki mapowania NHibernate-a. Sekcja HibernateProperties zawiera standardowe parametry konfiguracyjne NHibernate-a. Tutaj określiłem dialekt oraz driver bazy danych.

Przejdziemy teraz do wywołania właściwego kodu. NHibernate zawiera co najmniej trzy sposoby na wykonanie operacji na naszych zmapowanych obiektach. Pierwsze podejście opiera się na HibernateTemplate. To klasa narzędziowa pozwalająca na wywołanie standardowych metod CRUD sesji NHibernate. Spójrzmy na przykład: 

public class AbonentDAO : IAbonentDAO
{
  private HibernateTemplate hibernateTemplate;
  ISessionFactory SessionFactory
  {
     set { hibernateTemplate = new HibernateTemplate(value); }
  }
  public void Zapisz(Abonent abonent)
  {
    hibernateTemplate.SaveOrUpdate(abonent);
  }
}    

Widzimy prosty obiekt typu Data Access Object dla uproszczenia zawierający tylko metodę Zapisz. Wewnątrz metody jest wywoływane standardowe SaveOrUpdate z sesji NHibernate.
Jedyne co musimy dostarczyć to SessionFactory – oczywiście możemy to zadanie zlecić kontenerowi IoC. Fragment pliku konfiguracyjnego, który pozwoli wstrzyknąć instancję zdefiniowanego wcześniej SessionFactory do naszego DAO, wygląda tak:

 <object id=”AbonentDAO” type=”BizDev.SimpleSpringApplication.AbonentDAO, BizDev.SimpleSpringApplication„>
  <property name=”SessionFactory” ref=”MySessionFactory”/>
</object>

NHibernateTemplate dostarcza wiele metod z interfejsu Session NHibernate-a, jeśli jednak zechcemy wywołać jakąś metodę Session, której nie ma w NHibernateTemplate, to jest także na to sposób. Taka metoda naszego obiektu mogłaby wyglądać np. tak: 
public Abonent PobierzAbonenta(Guid idAbonenta)
{
  return nhibernateTemplate.Execute(
    delegate(ISession session)
    {
      return session.CreateQuery(”from Abonent a where a.Id = : idAbonenta”)
        .SetGuid(”idAbonenta’, idAbonenta’)
        .UniqueResult<Abonent>();
    }
  );
}      

 

Anonimowy delegat pozwala nam wywołać dowolny kod. To pierwsza z metod pozwalająca operować na obiektach. Alternatywnym sposobem jest dziedziczenie z klasy HibernateDaoSupport. Główną zaletą tego rozwiązania jest to, że zwalnia nas z konieczności używania callback-ów. Każdy przyzna, że nie jest to zbyt czytelny fragment kodu. Spójrzmy na taki przykład:

public class AbonentDAO : HibernateDaoSupport, IAbonentDAO
{
  public void Zapisz(Abonent abonent)
  {
    ISession session = DoGetSession(false);
    session.SaveOrUpdate(abonent);
  }
}  

 

Jednak mój ulubiony sposób operowania obiektami to trzeci sposób zgodnie z dokumentacją nazywany „contextual Sessions”, gdzie NHibernate sam zarządza jedną sesją ISession na transakcję. Taka metoda jest możliwa do zastosowania od wersji 1.2 NHibernate-a.  Zalętą tego rozwiązania jest to, iż nie musimy dziedziczyć po żadnej klasie Spring.NET. Przykład kodu poniżej.

 

public class AbonentDAO : IAbonentDAO
{
  private ISessionFactory sessionFactory;
  public ISessionFactory SessionFactory
  {
    get { return sessionFactory; }
    set { sessionFactory = value; }
  }
  public void Zapisz(Abonent abonent)
  {
    SessionFactory.GetCurrentSession().SaveOrUpdate(abonent);
  }

Jedyne co nam pozostaje to tylko wstrzyknięcie referencji do zdefiniowanego wcześniej SessionFactory do naszego DAO.
Przestawione tu informacje prawie wystarczają do rozpoczęcia pracy z NHibernate w Spring.NET. Dobrze wiemy, że prawie robi dużą różnicę ;). Niektórzy z Was zapewne spostrzegli, że nigdzie nie pojawił się tu temat transakcji. Zupełnie celowo, w tej jak i poprzedniej części, zarządzanie transakcjami zostało przeze mnie pominięte. Tematowi temu planuję poświęcić kolejną część tego cyklu.
Aby nie wyszło na to, iż przedstawiłem tu tylko suchą teorię, która ma się nijak do rzeczywistości, zaprezentuję w pełni działający przykład. Przykład zawiera jeden obiektu typu DataAccesObject o nazwie AbonentDAO, który realizuje proste operacje CRUD za pomocą NHibernate. Pominę definicję encji Abonent jak i treść pliku mapowania. Oto plik konfiguracyjny dla w pełni działającego przykładu:

   <spring>
    <parsers>
      <parser type=”Spring.Remoting.Config.RemotingNamespaceParser, Spring.Services” />
      <parser type=”Spring.Data.Config.DatabaseNamespaceParser, Spring.Data” />
      <parser type=”Spring.Transaction.Config.TxNamespaceParser, Spring.Data” />
    </parsers>
    <context>
      <resource uri=”config://spring/objects” />
    </context>
    <objects xmlns=”http://www.springframework.net&#8221;
             xmlns:db=”http://www.springframework.net/database&#8221;
             xmlns:tx=”http://www.springframework.net/tx&#8221;

>
      <db:provider id=”DbProvider”
                   provider=”SqlServer-2.0″
                   connectionString=”Data Source=BENIAMINZ\SQLEXPRESS;Initial Catalog=ADMINET;Integrated Security=SSPI;”/>
      <object id=”NHSessionFactory” type=”Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12″>
        <property name=”DbProvider” ref=”DbProvider”/>
        <property name=”MappingAssemblies”>
          <list>
            <value>BizDev.SimpleSpringApplication</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”/>
            <entry key=”hibernate.current_session_context_class”
              value=”Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate12″/>

          </dictionary>
        </property>
      </object>
     
      <object id=”transactionManager”
        type=”Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12″>
        <property name=”DbProvider” ref=”DbProvider”/>
        <property name=”SessionFactory” ref=”NHSessionFactory”/>
      </object>

      <object id=”AbonentDao” type=”BizDev.SimpleSpringApplication.AbonentDAO, BizDev.SimpleSpringApplication”>
        <property name=”SessionFactory” ref=”NHSessionFactory”/>
      </object>
 
      <tx:attribute-driven/>

    </objects>
  </spring>

Dodaj komentarz »

Brak komentarzy.

RSS feed for comments on this post. TrackBack URI

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

Stwórz darmową stronę albo bloga na WordPress.com.

%d bloggers like this: