Monthly Archives: March 2009

Użyszkodnicy, czyli o co walczymy w każdym projekcie cz.I

Zgodnie z zapowiedziami przyszedł czas na bardziej zaawansowane rzeczy w Grails. Myślę, że HowToJava jest gotowe na przyjęcie pierwszych użytkowników.

Zagadnienie zarządzania uzytkownikami każdej aplikacji internetowej to tzw. core, czyli element należący do rdzenia aplikacji, który po napisaniu podlega raczej niewielkim modyfikacjom. Dlatego też tym bardziej trzeba ten temat przemyśleć. Z całą pewnością kwestię użytkowników trzeba wpierw podzielić na 2 ‘fronty’ – backend i frontend.

Backendem w tym przpadku nazywam zarządzanie użytkownikami poprzez administrację lub automatyczne działania kodu. Czyli po rejestracji takowy kod wyśle mejla aktywującego, albo też wyświetli moderatorowi dane delikwenta, który pragnie zarejestrować się w serwisie. Z kolei frontend to wszystko to, co może zrobić użytkownik ze sobą (zarówno ten niezarejstrowany, jak i z pełnią praw w serwisie). Nie jest to podział intuicyjny, ani też taki, który da się wyczytać w necie. Jednakże dobrze oddaje kroki, które po kolei będę wykonywał, aby zaimplementować funkcjonalność w serwisie.

Wyjdźmy od modelu. Bardzo to trąci DDD, o którym w kontekście całości Grailsów z pewnością kiedyś napiszę. Jednakże to, że trąci wcale nie oznacza, że to źle! Dobra, bo zabieram się jak do zabicia karpia 🙂 Oto co trzeba na pewno w aplikacji:

  • proces rejestrowania uzytkowników, a w nim:
    • formularz rejestracji, jego walidacja oraz zapis do bazy
    • poinformowanie użytkownika o powodzeniu/niepowodzeniu procesu rejestracyjnego ( e-mail, komunikaty na stronie zaraz po rejestracji)
  • stworzenie namiastki panelu administracyjnego, który pozwoli na zarządzanie użytkownikiem (zarówno w procesie rejestracji jak i jego dalszej działalności)
  • napisanie kodu, który obsłuży użytkownika po zalogowaniu na stronie (do tego oczywiście oddzielna funkcjonalność oparta na formularzu rejestracji), a konkretnie:
    • rejestracja użytkownika w zasięgu sesji aplikacji
    • zbudowanie mniej lub bardziej rozbudowanego ACLa
    • stworzenie funkcjonalności, w której użytkownik będzie mógł się poruszać (czyli reszty serwisu)

Jak już wcześniej wspomniałem piszę kolejne notki na bieżąco, zatem powyższa lista z pewnością jest niekompletna i ulegnie modyfikacjom. Da się jednakże zauważyć oczywistą rzecz – potrzebujemy na pewno stworzyć kod, który będzie reprezentował naszego użytkownika (lub kandydata na niego). Nie zamierzam póki co bawić się w bardziej zaawansowane rzeczy – rejestracja zatem będzie przebiegała po prostu poprzez wpisanie nicku (unikatowego), a także hasła (plus potwierdzenie), dodamy także delikatną CAPTCHE, aby spamerzy nie mieli zbyt łatwo. Antycypując kolejne kroki nadamy też każdemu użytkownikowi jakąś rolę, którą będzie miał w serwisie – na szybko będą to role pod tytułem ‘czytacz’, ‘zwykły user’ i ‘admin’ (to ja :). Oto klasa dziedzinowa dla użytkownika:

class Htj_Users {

    static mapping = {
        id column: 'user_id'
    }

     static constraints = {
         nick( blank: false, size: 5..40, nullable: false, unique: true )
         passwd(blank: false, maxSize: 32, nullable: false, password: true )
         role( blank: false, nullable: false )
     }

    String nick;
    String passwd;
    Htj_User_Role role;
}

No i oczywiście mikroskopijna klasa dla typu roli:

class Htj_User_Role {

  static mapping = {
      id column: 'role_id'
  }

  String role_name;
}

Tworzymy kontroler o wdzięcznej nazwie UserController. Wpisujemy do niego taki kod:

def scaffold = Htj_Users;

i można już pobawić się dzięki scaffoldingowi. Ja do bazy przez PMA dodałem tylko trzy kategorie (wcześniej wymienione) i jak zawsze rusztowanie pozwala rozeznać się czy wszystko gra. Póki co gra zatem na dziś to koniec, późno trochę. Następnym razem zaimplementujemy cały CRUD ‘z palca’ plus zainstalujemy i wykorzystamy pierwszy raz plugin do frameworka.

A może SCJP?

Ano może. Zaczynam się dość poważnie nad tym zastanawiać. Dlaczego? Już piszę…

Moje odczucia są przede wszystkim podyktowane moją dotychczasową ‘karierą’ (jak ja nie lubię tego słowa) jako programista PHP. Jak wygląda nauka tego języka raczej nie trzeba nikomu tłumaczyć – dynamiczne coś, które do tego działa nawet jeśli napisany kod woła o pomstę do nieba. Zasadniczo jest on również pierwszym językiem programowania dla wielu osób, co również nie dziwi zważywszy na ‘atrakcyjność wizualną’ (tzn. można stworzyć choćby stronkę swojej klasy czy osiedla, konsolowe komunikaty w czystym C są mniej porywające i nie da się na nie podrywać panienek :).

PHP jest też o tyle ciekawe, że wymaga zapoznania się (choćby podstawowym) z całą otoczką programowania dla WEB2.0, czyli (pomijając już HTML&CSS) frameworkami JavaScriptu (plus cała technologia AJAX), serwerami (choćby tym Apaczem w wersji minimalnej), konsolą linuxową (przydaje się czasem), a także bazami. I ta wiedza z początku rzecz jasna nikła, z czasem stale się zwiększa, oparta nie tylko o przeczytane lektury, ale głównie o doświadczenie zawodowe. I dobrze. Problem polega jednakże na tym, iż często język zostaje niejako w tyle. Bo kiedy już opanuje się słowa kluczowe, najczęściej używane konstrukcje, zapamięta się tak na oko ze 30 podstawowych funkcji języka, wówczas zasadniczo następuje zatrzymanie i brak bodźców do dalszej nauki. Nie każdy lubi czytać opasłe changelogi z kolejnego ‘dziesiętnego’ wydania kompilatora. Nie kazdy też chce spędzić kilka dni/tygodni/miesięcy na zapoznawaniu się z olbrzymią listą funkcji zaimplementowanych w języku. Nawet jeśli już nie funkcji, to nawet elementów języka, których się nie zna, a poznane na przykład przy nauce innego języka wydają się być szalenie genialne.

Nie chce się, cóż począć? Ano można począć tyle, iż na samym początku nauki nowego języka rozejrzeć się za możliwością poznania go właśnie poprzez certyfikat. Uprzedzając pytania i zarzuty – wiem, że istnieje ZCE, ale blog jest o Javie i w tej technologii zamierzam się dalej rozwijać. Dlatego też korzystając z doświadczenia zaczynam bardzo poważnie zastanawiać się nad podejściem do SCJP (w najnowszej wersji rzecz jasna). Oczywiście jak to w życiu wszystko ma swoje plusy i minusy, oto lista:

Plusy Minusy
‘zmuszenie’ do zapoznania się z całością języka, spojrzenie na niego z szerszej perspektywy tak naprawdę to zabawa ‘kto wynajdzie błąd w maksymalnie pokomplikowanym kodzie’
możliwość poznania zagadnień, z którymi na co dzień raczej się nie styka no i skoro się nie styka to po cholerę się nimi zajmować?
mile widziane w CV zależne tylko od widzimisię rekrutera
ale jak już masz to uznają to na całym świecie z drugiej strony jak idziesz robić za granicę to raczej pikuś nie jesteś i coś tam umiesz
zdobycie konkretnej wiedzy, lepsze to, niż granie w CSa z drugiej strony czy na pewno przydatnej?

Tyle mniej więcej można napisać – posiłkując się wujkiem Google. Jednakże mnie chyba najbardziej rusza w tym wszystkim aspekt motywacyjny – po prostu chciałbym udowodnić sobie, że jestem w stanie nauczyć się czegoś w dziedzinie programowania, a następnie poddać to obiektywnej ocenie. Nie kończyłem studiów kierunkowych – zatem byłby to całkiem niezły fun. Z drugiej strony – ileż to ciekawych technologii nie będzie mi dane przez te przygotowania poznać (no albo znacznie się to opóźni). Przy założeniu, że myślę o potencjalnym poszukiwaniu pracy w Java-landzie za kilkanaście miesięcy, nie wiem czy zysk z SCJP zrekompensuje mi poświęcony na niego czas.

Zdaję sobie sprawę, iż spora część z czytaczy posiada ten certyfikat, albo mocno o nim przemyśliwuje – poprosiłbym zatem o komentarz jakowyś, albo też mejla, czy warto w mojej sytuacji podejmować się tego wyzwania? Zobaczę co napiszą mądrzejsci ode mnie – może po zakończeniu przygody z Grailsami wezmę się za ‘powrót do korzeni’.

Dla zainteresowanych podaję kilka linków, pod którymi można znaleźć ciekawe informacje dotyczące egzaminu, a także materiały do nauki:

HowToJava w sieci!

Tak to prawda. Myślałem na początku swojej przygody z Grails, że trochę trudno będzie pokazać cokolwiek szerszej publiczności – hosting Javy do tanich specjalnie nie należy, a ja szczerze powiem, iż wydawanie kilkuset (kilku tysięcy) złotych rocznie w celu quasi-hobbystycznym po prostu jest nieracjonalne. Na szczęście znowu Jacek Laskowski został moim bohaterem dnia.

Otóż istnieje coś tak miłego, jak serwis http://mor.ph, który umożliwia umieszczenie projektu Grailsowego (i nie tylko) na swoich serwerach, a także pozwala na sprawne zarządzanie nim. Cudownie! Od dziś zatem – postępy w pracach możecie śledzić na bieżąco pod adresem: http://howtojava.morphexchange.com. Proszę o wybaczenie jeśli coś nie działa lub się sypie – o to przecież chodzi, wszak wersja nawet nie alpha.

Dla tych, którzy chcieliby również skorzystać z serwisu podaję garść użytecznych informacji. Szybki tutorial jak wrzucić wszystko na serwer (wraz z deploymentem lokalnie) można wyczytać u Jacka. Raczej nie powinno być większych problemów (jakby były to piszcie – odpowiem). Ostrzegę tylko przed kilkoma pułapkami:

  • Należy uważać na plik z konfigiem do bazy. U mnie (MySQL) wygląda on tak i radzę go skopiować bezpośrednio i zmodyfikować. Literówki w deploymencie kosztowały mnie chyba godzinę wysyłania kolejnych wersji.
    production {
    dataSource {
    dbCreate = "update"
    url="jdbc:mysql://<NAZWA_HOSTA>/<NAZWA_BAZY>"
    username="<NAZWA_USERA>"
    password="<HASŁO_DO_BAZY>"
    }
    }

    Oczywiście loginy i hasło wraz z hostami dotyczą danych dla serwisu mor.ph. Wyczytać je można po zalogowaniu w odpowiedniej zakładce. Należy zwrócić uwagę a to, iż dbCreate to update, a także, że ja przeniosłem swój SQL ręcznie poprzez wklejenie kodu do PMA.
  • serwis działa wolno – obecnie (okolice 2-3 w nocy) wysyłanie kodu na serwer jak i deployment trochę trwa (a łącze mam 4MBit).
  • samo wysłanie na serwer nic nie gwarantuje – należy odczekać chwilkę, aż aplikacja nam “chwyci”.

To tyle na szybko. Zapraszam do krótkiej wizyty (zakładka Linki oraz Strona). Na razie to bardzo mało i niewiele, no ale i Google nie od razu napisali.

Koniec z linkami, czyli kontroler i widok pod kontrolą

Dziś ostateczne podsumowanie prac nad linkami w “How to Java”. Rzecz malutka, prosta, ale dająca dość obszerny obraz możliwości Grailsów. Na zakończenie szybki rzut oka na kontroler i troszeczkę większy na widok z użyciem GSP.

Jak pisałem w pierwszych postach o “How to Java” – statyczną zawartość umieszczę w jednym kontrolerze – StaticController, dodając ładne mapowanie adresów. Przyszedł czas by spełnić te obietnice. Oto kod kontrolera:


class StaticController {def defaultAction = 'linki'def linki = {[ linki: Htj_Links.list( sort: 'category') ]

}

def strona = { }

}

Jak widać definiujemy dwie akcje – strona oraz linki. Ta druga akcja będzie typowo statyczna – po prostu zostanie zaserwowany statyczny plik GSP. Sama definicja nie jest w tym przypadku konieczna – do tego jednakże wątku powrócę za chwilkę. Zastanowić się wypada co się stanie, kiedy w aplikacji odwołamy się do samego kontrolera, bez podania akcji. Standardowe mapowanie adresów w Grailsach nie wymaga podania akcji, konwencja przejmie tutaj sterowanie:

  • jeżeli w kontrolerze znajduje się tylko jedna akcja, wówczas domyślnie staje się ona podstawową akcją wywoływaną przy podaniu tylko nazwy kontrolera.
  • jeśli z kolei w kontrolerze mamy kilka akcji, domyślnie podstawową staje się ta o nazwie index
  • ostatecznie można tę akcję określić jawnie poprzez taki oto kod:
    def defaultAction = 'nazwaAkcji'

Zatem w kontrolerze dopisałem pro forma taką linijkę, choć docelowo kontroler Static nie będzie widoczny w aplikacji – jego użycie zostanie zakryte przez mapowanie. Teraz może kilka słów o akcji strona. Ma ona służyć wyświetleniu statycznego dokumentu HTML, w którym napisane byłoby kilka słów o stronce “How to Java”. Tak jak napisałem – jeżeli chodzi o wyrenderowanie widoku, bez konieczności jakichkolwiek działań w kontrolerze można ten przypadek obsłużyć inaczej. Należy dobrać się do pliku UrlMappings.groovy i dopisać tam następującą regułę:

"/strona" (view: "strona", controller: "static" )

I od dzisiaj przy wpisaniu po adresie: “/strona” nasze żądanie po prostu wyrenderuje wskazany widok, bez konieczności deklarowania akcji w kontrolerze. Możemy zatem na spokojnie usunąć linijkę z kontrolera.

Na sam koniec zostawiłem linki, gdyż tutaj sprawa jest poważniejsza. O przygodach z klasami domenowymi, które służą do obsługi tychże linków pisałem w poprzednich postach. Teraz zaś weźmiemy się za temat na poważnie:

[ linki: Htj_Links.list( sort: 'category') ]

Linijka ta robi tak wiele. Innymi słowy – wywołuje statycznie metodę list, która listuje wszystkie rekordy z tabeli. Do tego dorzucamy sortowanie po kategorii, a następnie to wszystko jest pakowane do widoku ( nawiasy kwadratowe na końcu akcji to po prostu zbiór zmiennych, które są przypisywane do widoku). Całkiem nieźle jak na krótką linijkę kodu.

Z kolei widok to w połowie otoczka związana z renderowaniem layoutu, zaś nas interesuje samo sedno:


<body><g:if test="${linki.size() > 0}" >
<ul>
<g:each in="${linki}" var="link"><g:if test="${link.category.category_name != poprzedniaKategoria}">
</ul>
<h2>${link.category.category_name}</h2>
<ul>
</g:if><li><a href="${link.address}">${link.title}</a> - ${link.description}</li>
<g:set scope="page" var="poprzedniaKategoria" value="${link.category.category_name}" />
</g:each>
</ul>
</g:if>

</body>

Po kolei. Na samym początku wykorzystujemy znacznik instrukcji warunkowej <g:if>, który sprawdza rozmiar przekazanego zbioru danych. Zasadniczo czynię to tylko po to, aby w razie pojawienia się pustego zbioru nie został wyrenderowany znacznik UL, który nie może być pusty jeśli chcemy pozostać w zgodzie z walidatorami HTML. Tworzymy listę, po czym przechodzimy po przekazanym nam zbiorze danych. I tutaj mała kwestia – kolejna instukcja IF. Rzecz w tym, iż w linkach są kategorie, głupotą zatem byłoby wrzucać wszystkie linki bez składu i rozkładu. Stąd instrukcja warunkowa, która sprawdza, czy poprzednia kategoria linku (zapisywana za pomocą znacznika <g:set>) była inna od obecnej. Jeśli była inna, wówczas zamykamy sobie listę, wyświetlamy nagłówek i tworzymy nową listę. Może i rozwiązanie to nie poraża geniuszem, ale swoją funkcję pełni wyśmienicie. W rezultacie po wprowadzeniu tego kodu (do bazy dodałem trochę danych przez PMA) u mnie przeglądarka wyświetliła coś takiego:

listalinkow

Piękne prawda? I to wszystko osiągnąłem dzięki relatywnie bardzo małej ilości kodu. Jedyne co może denerwować to brak porządku w nazwach linków w konkretnej kategorii – segregowanie językowe powinno na górę wypchnąć linki do Grailsów, zaś do mojego bloga na końcu. Dodamy zatem jeszcze jeden warunek do kontrolera:

[ linki: Htj_Links.list( [sort: 'category', sort: 'title' ]) ]

i możemy cieszyć się z ładniutkiej listy linków. Czysto, schludnie i bezpiecznie. Na dziś to tyle – w następnych wpisach zajmiemy się czymś bardziej dynamicznym – rejestracją użytkowników oraz ich autoryzacją.

Groovy in action, czyli to co tygrysy lubią najbardziej

Zdałem sobie sprawę, iż powoli w swoich postach wychodzę poza klasy dziedzinowe/domenowe i z pola związanego z GORM wypada przejść do bardziej ‘programistycznych’ rzeczy, czyli do kontrolerów i widoków. I co z tego?

Ano to z tego, iż w tych elementach MVC przyda się kilka słów wyjaśnienia dotyczących języka Groovy. Prostota Grailsów umożliwia rozpoczęcie z nimi pracy bez lepszej znajomości języka Groovy. Jednakże jest to taka sama sytuacja jak w przypadku RoRa – pewnym momencie nieznajomość języka bazowego doprowadza do zatrzymania w rozwoju, ba, wręcz konieczności cofnięcia się w nauce. Dlatego też uprzedzając ten stan rzeczy dziś artykuł dotyczący kilku charakterystycznych dla Groovy’ego konstrukcji i rozwiązań.

Na pierwszy ogień idąc domknięcia (ang. closures). Jak piszą w mądrych książkach – “Domknięcia nie są trudne, są inne.” Myślę, że jest to najlepszy możliwy ich opis. Mówiąc zaś w skrócie – domknięcia są to obiekty, które zachowują się jak metody. Mogą przyjmować parametry, mogą zwracać wartość. Takie pomieszanie z poplątaniem, ale dzięki temu jest to konstrukcja kosmicznie wręcz skalowalna i efektowna (o efektywności pewnie kiedyś napiszę). Drugą definicją może być proste stwierdzenie, iż domknięcie to zwykły obiekt, który jest związany z pewnym fragmentem kodu. Jest to może mniej inticyjne, ale mam nadzieję, że przykłady pokażą o co chodzi.

Domknięcia można tworzyć tak:
– “w przelocie”
suma = 0
(1..10).each{ cyferka -> suma += cyferka }

– “z przypisaniem do zmiennej” (do tej pory używane przeze mnie w przykładach)
suma = 0
def domkniecie = { cyferka -> suma += cyferka }

Rozebrać należy ten temat na czyniki pierwsze. Otóż naturalnym odruchem dla programisty Groovy’ego powinno być myślenie – “nawiasy klamrowe, o, domknięcie”!!! Niezależnie od użytej formy ich tworzenia, zawsze jest to domknięcie. Konstrukcja ze strzałką przypomina PHP, ale jej znaczenie i istota jest daleka od odwoływania się do obiektu w PHP. Pewnie zresztą już jest jasne o co chodzi – wszystko, co jest po lewej stronie strzałki to parametry, które są przekazywane do domknięcia (jako parametry metody). To zaś, co znajduje się po prawej stronie strzałki to najzwyklejsze ciało metody. W przypadku domknięć do których przekazujemy tylko jeden parametr, nie wymaga on deklaracji, zaś w kodzie można odwoływać się do niego za pomocą domyślnej zmiennej it, np:

def domkniecie = { println it }
domkniecie("Tutaj chlebik");

Istnieje też trzecia możliwość stworzenia domknięcia. Wystarczy, że dysponujemy metodą w klasie, np:

class Rysowanko {

public void narysujCos( String tekst ) {
System.out.println( tekst );

}
}

To by potraktować tę metodę jako domknięcie wystarczy taka konstrukcja:

Rysowanko egzemplarzDoRysowania = new Rysowanko();
def rysujaceDomkniecie = egzemplarzDoRysowania.&narysujCos;
rysujaceDomkniecie( "Tutaj chlebik po raz drugi!" );

Czyli tworzymy domknięcie w formie pewnego typu wskaźnika do metody. Do czego to się może przydać? Ano na przykład mamy klasę taką jak powyższa, w której znajduje się kilka przeciążonych metod ( parametry to string, jakieś numerki, może jeszcze inne obiekty z własnymi metodami toString). Deklarujemy domknięcie dla tej przeciążonej metody i… i w zależności od przekazanych do domknięcia parametrów wywołuje ono konkretną metodę. Miłe, fajne i przyjemne. Zainteresowanych tematem odsyłam do dokumentacji Groovy, gdzie można sobie na ten temat poczytać więcej.

Innym dość ciekawym elementem języka są operatory specjalne. Znacząco potrafią ułatwić życie programiście, zwłaszcza w kontekście kontrolerów i widoków. Oto one:

  • spread operator ( *. ) – oznacza w skróconym zapisie, iż należy daną metodę/domknięcie wykonać na wszystkich elementach zbioru na rzecz którego została ona wywołana. Ufff, zamotałem. Oto kod:
    def lista = ["Chlebik ", "to ", "kiepski ", "programista."]
    print( lista*.toString() )

    Wyświetli on zgodnie z oczekiwaniami wszystkie elementy listy w formie ciągu wyrazów oddzielonych spacjami
  • Elvis operator ( ?: ) – bardzo przyjemna rzecz, która pozwala nadać zmiennej domyślną wartość na wypadek, gdyby przez przypadek z przypisania wyszły nici (zwrócona wartość to null lub jest po prostu pusta). Kod:
    tekst = jestemObiektem.wyciagnijMojaNazwe()?:"nie wiadomo"
  • Null-safe deference ( ?. ) – pamiętacie te wszytkie konstrukcje IF NOT NULL (lub coś w tym guście)? W Groovy istnieje operator, który wyręcza nas w tej brzydkiej czynności. Oto kod:
    println( zmiennaMogacaBycNullem?.jakiesJejPoleLubMetoda )"
    Jeżeli potencjalny obiekt, którego metodę chcielibyśmy wywołać okaże się być NULL to po prostu nic nie nastąpi. Tyle.

Na sam koniec zostawiłem tzw. Expando. Kolejna rzecz z ‘dynamicznej’ części Groovy, która jednakże może budzić pewne obawy. Zwrócił na to uwagę Jacek Laskowski w swojej prezentacji na WJUGu. Ale do rzeczy. Expando to po prostu obiekt, który zachowuje się w sposób, który określamy ‘na bieżąco’, bez konieczności tworzenia oddzielnej klasy lub pliku. Wkleję może przykład z książki:

def user = new Expando()

user.firstName = 'Christopher'
user.lastName = 'Judd'

user.greeting = { greeting ->
"${greeting} ${firstName} ${lastName}"
}

assert user.greeting("Hello") == 'Hello Christopher Judd'

Jak widać do takiego Expando możemy przypisać pewne wartości, zaś zachowanie (metody) deklarujemy poprzez domknięcia. Świetna sprawa, zwłaszcza by nie zaśmiecać kodu klasami wewnętrznymi.

To tyle jeśli chodzi o ten ciekawy język. Oczywiście rzecz jasna poruszyłem raptem trzy zagadnienia, jednakże uczyniłem tak tylko z powodu wyjaśnienia kilku rzeczy, które na 100% pojawią się w kolejnych wpisach, a wiedza o nich jest konieczna. Zainteresowanych poznaniem Groovy od podszewki zapraszam na oficjalne strony projektu. Można też zaopatrzyć się w literaturę poświęconą zagadnieniu, albo też skorzystać z masy linków, które prowadzą do miejsc związanych z Groovy.

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.

Coś się chyba Helionowi pomyliło

Dziś o czymś troszeczkę innym niż Grails. Odwiedziłem stronę wydawnictwa Helion, coby przepatrzyć jakieś promocje czy nowości. Jakie było moje zdziwienie, kiedy na stronie głównej pośród nowości zobaczyłem tę oto książkę.

Jasno i wyraźnie stoi napisane, iż jest to TOM 2. Hmmm, a gdzie tom pierwszy? W swojej naiwności liczyłem na to, że może chodzi o książkę o JSF, którą miałem okazję zakupić i leży u mnie na półce. Rzeczywistość okazała się jednakże inna – nasze cudowne wydawnictwo przetłumaczyło i wydrukowało tylko drugi tom! Z ciekawości zajrzałem do googla i co się okazuje? Że tom pierwszy tejże książki można sobie spokojnie ściągnąć z internetu spod tego adresu. Wiadomo, jest też jakieś “ale” – w treści książki mamy materiały reklamowe (niezbyt dokuczliwe), a także nie da się ściągnąć wersji all-in-one. Jednakże i to jest świetna gratka dla każdego, kto chciałby poznać tę technologię bliżej. Do tej pory pozostawało tylko “Head First”, a niekoniecznie każdemu forma tej serii wydawniczej pasuje. Jak widać i z pomyłek mogą narodzić się konkretne korzyści 🙂