NHibernate lek na całe zło

Na wstępie  mała zmiana. Wróciłem na chwilę do języka polskiego bo posty w języku angielskim zajmowały za dużo czasu a konkurs za chwilę się kończy. W ostatnim wpisie dotyczącym projektu wspominałem o użyciu SQLite jako lokalnej bazy danych, która to ma ułatwić konfigurację dla mało obeznanych użytkowników. Wybrana będzie domyślna baza danych, jednak będzie możliwość stworzenia zupełnie czystej bazy. Taka funkcjonalność została wprowadzona w przypadku gdy chcielibyśmy zachować nasze poprzednie dane, ale zacząć od 0. W zasadzie jest to funkcja dla bardziej zaawansowanych użytkowników.

NHibernate co to takiego?

Jest to biblioteka, która służy do mapowania obiektowo-relacyjnego. Dzięki tej bibliotece w niezwykle prosty i przejrzysty sposób możemy zmapować logikę biznesową aplikacji ze strukturą bazy danych i odwrotnie. Dlaczego NHibernate a nie Entity Framework? W zasadzie sam nie znam odpowiedzi na to pytanie. Po prostu zacząłem zagłębiać się w NHibernate i tak już zostało. Na początku może odstraszyć konfiguracja (też miałem na początku problemy ze zrozumieniem) ale jak to się mówi wszystko jest dla ludzi. W internecie można znaleźć sporo przykładów, które dość dobrze opisują krok po kroku co należy wykonać.

Słowo wstępu

Nie chciałem od razu implementować biblioteki w projekcie bo nie wiedziałem jak się zachowa, czy nie posypie się cały projekt – choć w zasadzie niewiele mogłoby się posypać bo projekt nie jest mocno rozwinięty ;(  No trudno. Stworzyłem sobie osobny projekt konsolowy (po co zagłębiać w GUI – de facto jest to swego rodzaju test bez testu :p ma to sens? w pewnym sensie tak). Na początek najlepiej byłoby wiedzieć jak będzie wyglądała nasza baza danych. Na starcie skupię się na 1 tabeli jobs, czyli tabla z naszymi zadaniami. W tym momencie możemy od razu stworzyć plik o nazwie hibernate.cfg.xml, w którym znajdować się będzie konfiguracja NHibernate. Build action powinno być Content natomiast Copy to Output Directory Copy if newer. Mój plik wygląda następująco (w przypadku korzystania z SQLite).

W przypadku korzystania z innego typu bazy danych musimy zmienić dialect oraz driver_class na odpowiednie dla danego typu bazy (należy poszukać odpowiedniej konfiguracji).

Tworzenie modelu

Każda z tabel musi posiadać logikę biznesową, czyli klasę która będzie odzwierciedleniem tabeli Job w kodzie. Na tym etapie musimy już wiedzieć jakie kolumny i jakiego typu będzie posiadać nasza tabela. Najlepiej byłoby utworzyć osobny katalog, w którym będziemy przechowywać wszystkie nasze modele. Moja klasa wygląda następująco:

Zawarłem wszystkie niezbędne kolumny dla tabeli z zadaniami. Możemy teraz przejść do mapowania.

Mapowanie

Z przykładów jakie znalazłem widziałem dwa sposoby mapowania. W pliku xml oraz w kodzie. Bardziej przejrzystym sposobem dla mnie jest mapowanie w kodzie i z takiego skorzystałem. Tworzymy folder Mappinggdzie będziemy przechowywać pliki mapowania naszych tabel. Następnie tworzymy plik, który będzie zawierał definicje.

Jak widzimy mamy tutaj dodanie do mappera JobMap, zatem należy go stworzyć i tam właśnie zawarte będą definicje mapowania. Póki co wykonałem proste mapowanie z autoinkremetującym się ID zadania oraz pozostałymi kolumnami, jednak bez żadnych ograniczeń typu NOT NULLJak wygląda zatem nasz plik mapowania tabeli Job. Oto on:

Jak widzimy na powyższym przykładzie do podstawowych zastosowań jest to niezwykle proste (może pomijając autoinkrementację id :p).

Tworzenie konfiguracji i bazy

Do tego celu stworzyłem plik NHibernateHelper.cs. Plik ten inicjuje połączenie oraz wykonuje mapowanie i tworzy sesję. Nie będę tutaj zamieszczał całego kodu bo jest to dość rozległy plik zapraszam na mojego GitHuba. Jedną z ważniejszych rzeczy w tym pliku jest zmienna CONNECTION_STRING. Określa ona do jakiej bazy danych (jakiego pliku) chcemy się połączyć. Gdyby nie możliwość tworzenia nowych nie byłoby takiej potrzeby i można by było connection_string na sztywno zapisać w pliku konfiguracyjnym NHibernate. Metoda tworząca konfigurację i deseralizująca mapowanie:

Tworzymy tutaj nową instancję konfiguracji. Na wstępie sprawdzamy czy istnieje plik bazy danych, który będziemy mieć zapisany w ustawieniach. Jeśli nie, to tworzymy plik. Na chwilę obecną jest to przypisany na sztywno plik o nazwie kopstt.db, jednak domyślnie plik ten będzie zaczytywany z pliku konfiguracyjnego aplikacji. Najważniejsza w takim wypadku jest linia 9 powyższego kodu, która definiuje połączenie do bazy. Dopiero po tym kroku możemy wykonać polecenie Configure().

Połączenie z bazą

Mając wszytko co powyżej, możemy stworzyć już naszą bazę. Możemy zrobić to w dowolnym miejscu w aplikacji. Stworzyć dodatkowy przycisk lub przy inicjalizacji okna. Najlepiej byłoby w teście (no ale to już wyjaśniałem dlaczego nie tym razem), lub ewentualnie stworzyć osobną aplikacje konsolową. A samo wywołanie połączenia wygląda następująco:

SchemaUpdate aktualizuje nasz schemat bazy danych na podstawie naszej konfiguracji natomiast Execute jak sama nazwa wskazuje wykonuje polecenie.

Gdy uruchomimy powyższe polecenie sprawdzamy czy w folderze posiadamy plik kopstt.db. Jeśli tak to wszystko poszło jak należy ale dla pewności możemy sprawdzić poprawność naszej bazy za pomocą programu DB Browser for SQLite. Klikamy Open Database, wybieramy nasz plik i naszym oczom powinno ukazać się następujące okno.kopstt.db

Jak widzimy baza została poprawnie utworzona.

Na zakończenie

Chcąc nieco ułatwić sobie przenoszenie danych między różnymi typami bazy danych musiałem porzucić poprzednie podejście tworzenia bazy danych i de facto zacząć wszystko od nowa. Myślę jednak, że było warto, gdyż kod stanie się dzięki temu bardziej przejrzysty i bardziej przyjazny. A po co tworzyć coś od nowa jeśli już są do tego gotowe biblioteki?

  • dariol

    “NHibernate lek na całe zło” – heh… też tak kiedyś myślałem 🙂

    • http://www.gemustudio.com blaze

      I zmieniłeś zdanie? Korzystasz z czegoś innego?