Koniec sprzątania w modelu

Ostatnio posprzątaliśmy w modelu. I dobrze. Jednakże sprzątaliśmy malutki zaledwie modelik, taki naprawdę tyci tyci. Dzisiaj rozszerzymy go do wersji ostatecznej – gotowej do wdrożenia na serwer produkcyjny.

Mowa oczywiście o klasie Htj_Links(). Skończyłem na tym, iż posiada ona swoje ID (zmapowane za pomocą GORMa na kolumnę w tabeli bazy o nazwie ‘link_id’), adres oraz krótki opis. Dla ułatwienia poznawania frameworka zrezygnowałem w przykładach z odnoszenia się do kategorii linków, które miały być przechowywane w oddzielnej tabeli w bazie. Czas coś z tym zrobić.

Kod klasy domenowej, która opisywać będzie kategorię wygląda następująco:

class Htj_Links_Category {

static mapping = {
table 'htj_links_category'
id column: 'category_id'
}

String category_name;

}

Jak widać również zdefiniowałem nazwę tabeli (jakoś tak nie potrafię zostawić tego samej konwencji), a także kolumnę z kluczem głównym. I tak się teraz zastanawiam. W pliku DataSource.groovy zdefiniowałem typ interakcji z bazą (parametr dbCreate na update) – ciekawe co wyniknie z tego, że nie utworzyłem w bazie tabeli o nazwie zadeklarowanej w mapowaniu. Cóż, małe dopisanie linijki do pliku Htj_Links():

Htj_Links_Category category_id;

I uruchamiam aplikację. Błędem nie sypnęło, aplikacja uruchomiona. Pod adresem, gdzie zostawiłem wylistowanie wszystkich rekordów z bazy i owszem – wylistowało, ale… Tabela htj_links w bazie wzbogaciła się o kolumnę category_id_id (dlaczego tak???) o wartości NULL. Ciekawe. Tablica htj_links_category oczywiście nie została utworzona. No to teraz wracamy do pliku konfiguracyjnego i klepiem dbCreate na create. Mhhhhhmmmmmm, miodzio. Po ponownym rozruchu w bazie pojawiła się tabela htj_links_category z polami:

  • category_id
  • category_name (varchar(255))
  • version (automatycznie dodane przez GORMa

Całkiem całkiem jak widać. Ciekawi mnie tylko wciąż ta dziwna nazwa kolumny w tabeli htj_links. Po namyśle jednakże doszedłem do wniosku, iż skoro jest to egzemplarz klasy domenowej, to pewnie jest kluczem obcym, ergo, do podstawowej nazwy dodaje się suffix ID. Zatem dla ciekawości – usuwam z bazy htj_links_category, a także kolumnę z tabeli htj_links plus zmieniam nazwę pola w klasie Htj_Links na category. I ZONK…

ISTOTNE!!!

Po stworzeniu tabel i wspomnianej kolumny potworzone zostały automatycznie referery na kolumny z kluczami głównymi. Innymi słowy – tabela htj_links_category posiadała własny klucz główny, a oczywiście category_id_id zrefererowało do tegoż klucza, ale ZANIM TEN KLUCZ POWSTAŁ. Dlaczego o tym piszę? Ano dlatego, że próba ręcznego usunięcia tabeli htj_links_category lub kolumny category_id_id skutkowała błędami MySQL. W końcu tabelę usunąłem ręcznie z dysku, a po tej operacji kolumnę z kluczem obcym usunąłem poprzez wywołanie polecenia SQL:

ALTER TABLE htj_links DROP FOREIGN KEY FKDD69A9183B1B60E3;

Po ponownym rozruchu kolejny ZONK – tabela htj_links_category nie raczyła się utworzyć! Ciekawe… No nic – stworzyłem ją ręcznie (tylko klucz główny na category_id i kolumna VARCHAR dla category_name, zaś w DataSource.groovy powróciłem do opcji uaktualniania bazy danych. Rozruch – no i dało radę. Nie wiem czy to mój przypadek, niedoskonałość GORMa, czy jeszcze coś.

Idźmy jednakże dalej (i to pewnie też rozwiąże problem napotkany powyżej). Domyślam się, iż nasz piękny framework z góry założył, że chodzi o relację jeden-do-wielu (patrząc z perspektywy kategorii linku) i dlatego tak to wygląda (klucz obcy tylko w tabeli htj_links). Nie dodaję jakichkolwiek parametrów zawierania i przynależności (belongsTo lub hasMany, gdyż to nie jest tutaj potrzebne). Ręcznie dopisałem do tabeli htj_links_category rekord ( ‘nazwa’ to ‘java’, zaś id zostało wygenerowane automatycznie), po czym wpisałem również ręcznie odpowiedni ID do kolumny category_id w tabeli htj_links. I teraz w kontrolerze wystarczyło dodać taki oto kod:

def lineczki = Htj_Links.list()
lineczki.each{ println(it.category.category_name) }

I wykona się z automatu piękny JOIN i na konsoli powinniśmy otrzymać cudowny wynik w postaci: ‘java’. I o to chodziło! W następnym odcinku zajmę się ubraniem tego wszystkiego w ładniejszy widok.

Advertisements

6 thoughts on “Koniec sprzątania w modelu

  1. Mateusz

    “Domyślam się, iż nasz piękny framework z góry założył, że chodzi o relację jeden-do-wielu” – framework nic nie zaklada. To Ty zdefiniowales, ze Htj_Links ma jedno pole Htj_Links_Category. Wiec jest to jeden-do-jednego. Gdybys uzyl hasMany wtedy mialbys jeden-do-wielu. Nie zdefiniowales tez belongsTo, wiec nie wiadomo, ktora strona relacji jest “owning”.

    Tak jak to zadeklarowales, to jest jednokierunkowa relacja jeden-do-jednego (http://grails.org/doc/1.1/guide/5.%20Object%20Relational%20Mapping%20(GORM).html#5.2.1.1 One-to-one) przyklad A.

    “Htj_Links_Category category_id;” – z tego co napisales w poprzednim poscie, takiego nazewnictwa uzywasz dla kluczy, tak? Co w takim razie chciales tu zadeklarowac? Widze dwie opcje:
    Htj_Links_Category category; // deklarujesz encje i traktujesz to jako encje (aka klase domenowa)
    int category_id; // sam deklarujesz klucz obcy i sam go obslugujesz

    Wydaje mi sie, ze Twoje przygody komplikuja sie z powodu mieszania pojec bazodanowych a obiektowych. Proponuje, zebys skupil sie na modelu obiektowym zupelnie nie myslac na poczatek o bazie danych. Zapomnij na moment, ze obiekty beda persystentne i gdziekolwiek zapisane – powinno to ulatwic zrozumienie, jak taki model wyglada, jak go zadeklarowac wg. zasad obiektowych. Pozniej bedzie bardzo latwo Ci przejsc na pojecia bazodanowe, przedeklarowac nazwy kolumn, itp.

    Jak masz jakies pytania smialo pisz, postaram sie pomoc.

  2. chlebik Post author

    Może i to wynika z tego, iż do tej pory w jakimkolwiek pisanym przeze mnie projekcie nie stosowałem pełnego ORMa – albo firma i sam projekt nie był na tym poziomie, albo też nie robiono tego ze względów wydajnościowych (narzut takiego Doctrine’a kosztuje czas procka i RAMu). Jednakże są dwie możliwości jak mniemam:
    – albo tworzymy obiekty ze wszystkiego co się da (mój przykład), a to skutkuje również klasami domenowymi wszędzie gdzie się da
    – albo też tworzymy obiekty grube, zaś duperelkowate cechy poboczne (jak choćby ta kategoria trzymana gdzieś tam w małej tabelce w bazie) obsługujemy za pomocą obiektów tylko na CRUDzie, zaś ich relacje z innymi obiektami (u mnie Htj_Links) obsługuję ‘ręcznie’ na zasadzie kluczy obcych w formie liczby i wyciąganie ich dokonywać np. za pomocą składni z kryteriami wyciągania?

    Tak to widzę – jeśli jest inaczej to proszę mnie uświadamiać, bo ja młody w te klocki jeszcze jestem.

  3. xis

    Zdecydowanie twórz obiekty ze “wszystkiego co się da” 🙂 Takim czymś jak “nazwa kolumny z kluczem obcym” w ogóle nie powinieneś się przejmować, bo – jak napisał Mateusz – to jest już warstwa bazy danych, do której Twoja wyobraźnia nie powinna w ogóle sięgać – po to są ORMy, żeby Cię od tego odciążyć. Po prostu link należy do jakiejś kategorii i napisz to tak, jak widzisz to w modelu obiektowym. Dobry (i dobrze pokierowany, a GORM taki właśnie jest) ORM poradzi sobie z modelem obiektowym 🙂
    Żadne category_id, po prostu kategoria 🙂

    Ty, jako człowiek też masz “płeć”, a nie “płeć_id” (czyli obiekty wiążą się referencjami do innych obiektów, a nie do ich kluczy – klucz to tylko reprezentacja w bazie danych, która nas w tej chwili nie obchodzi).

  4. mati

    Młody w te klocki, ale sprawny i szybko się uczący 🙂

    Jest dokładnie tak jak napisałeś: albo operujemy na czystych obiektach, albo robimy to ręcznie. Tylko, że GORM i inne frameworki zostały stworzone po to, żeby operować właśnie na obiektach i nie robić pewnych rzeczy ręcznie (stąd ta cała konwencja”).

    Nie chodzi też o poziom firmy czy projektu, bo w niektórych przypadkach nie używanie ORM jest właśnie dowodem na wyższy poziom 🙂 Przejście na czysto obiektowe myślenie nie jest łatwe i sam chwile czasu na to porzebowałem.

    Pamiętaj natomiast, że jedno drugiego nie wyklucza. Mając czyste klasy domenowne, nadal możesz sobie przedefiniować klucze, nazwy kolumn, w Hibernate pisać zapytania HQL zamiast operować na asocjacjach, a w skrajnych przypadkach pisać zapytania natywne. Baza też nie musi być w 4 postaci normalnej, można przemapować klasy, zrobić klasy zagnieżdżone, różne strategie dziedziczenia, itp. itd. Po prosti jeśli zaczynasz przygode z ORM, myśle, że lepiej zacząć od czystej postaci, wykorzystać to co daje konwencja, nic nie zmieniać na start. A później w miare pracy i rozbudowania modelu można wejść w bardziej złożone sytuacje.

    A jeszcze a propo przedrostków w nazwach tabeli (Htj_) to zdziwiłem się, że Grails tego nie wspiera – ale mają na to otwarte zgłoszenie w bug trackerze.

    Jeszcze raz poCHLEBiam serię postów 😉 Bardzo fajne spojrzenie od “drugiej strony” na GORM.

  5. chlebik Post author

    Hmmmm już rozumiem co chcecie mi przekazać. Czyli po prostu postrzegać klasy domenowe i pracę GORMa jako modelowanie obiektowe w ogóle bez zabawy w implementację na poziomie innym niż klasa domenowa. Innymi słowy – Htj_Links_Category to klasa domenowa, TYLKO TAM należy bawić się w podawanie (lub nie) mapowania, tabel, kolumn, zaś gdziekolwiek indziej po prostu w klasie (Htj_Links) napisać, że każdy link ma jakąś tam kategorię, nazwać sobie tę własność jakkolwiek mi się podoba i tylko się do niej odwoływać w sposób bezpośredni.

    Ps. Problemu z takim myśleniem (już) nie mam, kwestia tylko potencjlanego ogarnięcia co czasem ORM po części ‘bez wiedzy programisty’ dodaje do tabel (bardzo często już istniejących), stąd moje ostrożne podejście do tematu.

  6. mati

    Dokladnie tak. Napisze Ci jak ja bym zrobil (uwzgledniajac zachowanie Twojej konwencji nazwenictwa dla tabel). Poki co zostawiamy relacje Link-Category jako jeden-do-jednego dla prostoty przykladu. Zamiana tego na jeden-do-wielu lub wiele-do-wielu bedzie potem prosta.

    class Link {
    String description
    Category category

    static mapping = {
    table ‘hjt_links’
    id column:’link_id’
    }
    }

    class Category {
    String name

    static mapping = {
    table ‘hjt_categories’
    id column:’category_id’
    }
    }

    Sa przedrostki dla tabel, sa nazwy dla idkow po Twojemu i jest obiektowo 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s