Uruchamiamy aplikację JPetStore dla Apache iBATIS Data Mapper
Parę dni temu, w artykule Wstęp do technologii Apache iBATIS Data Mapper opisałem podstawy iBATIS’a, dziś przyszła kolej, aby uruchomić pierwszą aplikację. Na stronie projektu mamy gotowy przykład, osławiony JPetStore. Zaczynamy od pobrania i rozpakowania pliku JPetStore-5.0.zip, powstaje katalog JPetStore-5.0. W podkatalogu build\wars znajduje się plik jpetstore.war. Aby pobawić się chwilę aplikacją wystarczy wrzucić ten plik na nasz ulubiony serwer aplikacyjny i tyle, działa bez konieczności wykonywania żadnych dodatkowych akcji, przynajmniej na GlassFish’u. Hmmm... nawet bez konfiguracji połączenia do bazy danych? A skąd biorą się dane, które widzimy w aplikacji? Otóż owszem, z bazy danych, ale powoli, zacznijmy od podpięcia kodu do jakiegoś miłego IDE. Z NetBeans’em 5.5 poszło gładko, w końcu udało się też wygrać z Eclipse’m ale nie obyło się tu bez żonglowania katalogami, więc pozostanę przy tym pierwszym. No więc odpalamy NetBeans > New Project > Web Application with Existing Sources > Next. Pojawia się kolejny ekran. Jako lokalizację źródeł projektu wybieramy katalog JPetStore-5.0, reszta pól wypełnia się automatycznie, wygląda to jakoś tak:
Widzimy komunikat o błędzie, który mówi, że w katalogu JPetStore-5.0 znajduje się podkatalog build co nie jest właściwe. Jako że to, co tam jest nie będzie nam już potrzebne podkatalog ten zwyczajnie kasujemy. Aby NetBeans zauważył zmianę klikamy Back, po czym Next i jeszcze raz Next. Upewnijmy się, że wszystko jest OK, tzn. tak jak na poniższej ilustracji. W szczególności, że jako katalog bibliotek NetBeans wykrył katalog lib a nie devlib.
Klikamy Finish i gotowe. Aby upewnić się, że wszystko jest w porządku uruchamiany projekt z poziomu IDE, tj. klikamy prawym guzikiem myszy na nazwie projektu i z menu kontekstowego wybieramy opcję Run Project. No dobrze, ale jak to jest z tą bazą danych? Zerknijmy na listę pakietów w gałęzi Source Packages. Pakiety zaczynające się od przedrostka ddl (ang. data definition language) to skrypty tworzące strukturę bazy danych i wypełniające tą bazę inicjalną porcją informacji. Jako że różne bazy danych posługują się różnymi dialektami SQL mamy różne wersje skryptów. Otwórzmy teraz plik database.properties z pakietu properties. Już widzimy, że aplikacja używa bazy danych HSQLDB, http://hsqldb.org/, i to w trybie Memory-Only Database, czyli tabele przechowywane są wyłącznie w pamięci operacyjnej. Oznacza to, że żadne dane nie są de-facto utrwalone, i że nie ma sensu poszukiwać gdzieś między plikami aplikacji pliku bazy danych. Zatem skrypty tworzące strukturę bazy danych i wypełniające ją danymi o zwierzątkach muszą być uruchamiane każdorazowo przy starcie aplikacji. I rzeczywiście tak jest. Aplikacja, oprócz iBATIS Data Mapper’a używa jeszcze paru innych bibliotek, w szczególności szkieletu (ang. framework) Apache iBATIS DAO, który do niedawna, tj. aż do wersji 2.2.0 był częścią Data Mapper’a, a przynajmniej był razem dystrybuowany. Obecnie są to osobne, zupełnie niezależne produkty z wyraźnym wskazaniem, że iBATIS DAO jest już nie fajny i że w jego miejsce należy stosować Spring’a. Nasza aplikacja do najnowszych nie należy i używa biblioteki iBATIS DAO, ale obejdzie się bez jej szczegółowej znajomości. Zerknijmy do pakietu com.ibatis.jpetstore.persistence. W klasie DaoConfig.java znajdziemy kod który uruchamia skrypty z pakietu ddl.hsql jeśli tylko zmienna url z wspomnianego wcześniej pliku database.properties ma wartość jdbc:hsqldb:mem:jpetstore. Plik dao.xml to konfiguracja dla biblioteki iBATIS DAO, jego zawartość jest w zasadzie samo opisująca się.
Przeszkadzajki mamy już za sobą, możemy przystąpić do przeglądania źródeł, a warto, bo można się z nich wiele nauczyć, także z tematów wykraczających poza technologie trwałości danych i Data Mapper’a. Zwracam jeszcze uwagę tych, którzy postanowią poeksperymentować i uruchomić aplikację na innej bazie danych, że może to wymagać drobnych zmian w SQL’ach. Ja spróbowałem na MySQL Server 5.0 który okazał się być bardziej rygorystyczny niż HSQLDB. Próba przeglądania szczegółów zwierzątka kończyła się bardzo wylewnym wyjątkiem, którego mały fragment wygląda następująco:
Caused by: com.ibatis.dao.client.DaoException: Failed to execute queryForObject - id [getItem], parameterObject [EST-20]. Cause: com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in com/ibatis/jpetstore/persistence/sqlmapdao/sql/Item.xml.
--- The error occurred while applying a parameter map.
--- Check the getItem-InlineParameterMap.
--- Check the statement (query failed).
--- Cause: com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Column 'ITEMID' in field list is ambiguous
I jest to bardzo miłe ze strony iBATIS’a, że mówi nam jasno, co jest źle. Niestety nie wszystkie biblioteki są tak łaskawe. Zgłosiłem powyższy błąd jako IBATIS-411.