25 października 2007

Porównanie mechanizmów stronicowania wyników zapytania

Istotnym elementem typowej aplikacji jest przetwarzanie, np. wyświetlanie, pewnej listy obiektów będącej wynikiem wyszukiwania w bazie danych. Bardzo często zdarza się, że jednorazowo potrzebny jest tylko pewien fragment tej listy, np. 10 najnowszych zamówień. Technologia trwałości danych powinna umożliwiać i ułatwiać pobieranie tylko pewnego podzbioru (strony) wyników zapytania i pobieranie kolejnych podzbiorów (stron) w miarę potrzeby. Poniżej pokażę, w jaki sposób implementację stronicowania wyników zapytania wspierają technologie (i specyfikacje) Hibernate, JPA, JDO, JDBC oraz Apache iBATIS Data Mapper. Jest to drugie z kryteriów porównania technologii trwałości danych, jakie tutaj prezentuję. Pierwszym było porównanie ze względu na wsparcie dla budowania dynamicznych zapytań. Wpierw, w artykule "Dynamiczne zapytania i selekcja poprzez przykład czyli Hibernate nokautuje JPA" porównałem Hibernate i JPA. Potem dodałem jeszcze wpis o iBATIS’ie w artykule "Dynamiczne zapytania z Apache iBATIS Data Mapper". Zapraszam do lektury, zwłaszcza, jeśli swoje wybory technologii lubisz opierać na konkretnych przesłankach.

Hibernate

Technologia Hibernate oferuje bardzo wygodny mechanizm stronicowania wyników zapytań. Mechanizm ten jest zaimplementowany na poziomie funkcji interfejsu programisty, nie na poziomie języka zapytań, stronicowanie rezultatu zapytania jest więc niezależne od treści samego zapytania. Fragment kodu pobierający drugą dziesiątkę zamówień (klasa Order) posortowanych po dacie złożenia zamówienia (atrybut placedDate) wygląda następująco:

Query query =
session.createQuery("FROM Order order ORDER BY order.placedDate");

query.setFirstResult(10);
query.setMaxResults(10);

List orders = query.list();

Uruchomienie powyższego kodu spowoduje wygenerowanie odpowiedniego zapytania SQL, uwzględniającego specyfikę danej bazy danych. Hibernate wspiera bardzo wiele relacyjnych baz danych i ich dialekty języka SQL.

JPA - Java Persistence API

Wsparcie dla stronicowania oferowane przez specyfikacje Java Persistence API jest niemal identyczne jak w przypadku technologii Hibernate. Fragment kodu pobierający drugą dziesiątkę zamówień (klasa Order) posortowanych po dacie złożenia zamówienia (atrybut placedDate) wygląda następująco:

Query query = eManager.createQuery
("SELECT order FROM Order order ORDER BY order.placedDate");

query.setFirstResult(10);
query.setMaxResults(10);

List orders = query.getResultList();

JDO - Java Data Objects

Technologia Java Data Objects wspiera stronicowanie wyników zapytania poprzez odpowiednie konstrukcje samego języka zapytań. Samo zapytanie może być wyrażone na dwa sposoby; w postaci pojedynczego obiektu klasy String definiującego jego treść lub poprzez ciąg wywołań odpowiednich funkcji interfejsu programisty. Stosując drugą z możliwości fragment kodu pobierający zamówienia (klasa Order) od 10. do 20. sortując po dacie złożenia zamówienia (atrybut placedDate) wygląda następująco:

Query query = pManager.newQuery(Order.class);

query.setOrdering("placedDate ASC");
query.setRange(10, 20);

Collection orders = (Collection) query.execute();

Możliwe jest także zdefiniowanie zakresu stronicowania jako zmiennych, a następnie wywoływanie wielokrotnie tego samego zapytania z podaniem różnych wartości. Analogiczna do powyższej funkcjonalność byłaby wtedy zaimplementowana następująco:

Query query = pManager.newQuery(Order.class);

query.setOrdering("placedDate ASC");
query.setRange(":1, :2");

Collection orders = (Collection) query.execute(10, 20);

Stosując podejście z zapytaniem wyrażonym jako pojedynczy napis analogiczny kod wygląda następująco:

Query query = pManager.newQuery
("SELECT FROM Order ORDER BY placedDate ASC RANGE :1, :2");

Collection orders = (Collection) query.execute(10, 20);

JDBC

Technologia JDBC służy do uruchamiania zapytań SQL i pobierania ewentualnych wyników tych zapytań. Zapytania są przekazywane do uruchomienia w niezmienionej formie, zatem technologia ta nie oferuje żadnych ułatwień dla implementacji stronicowania wyników zapytań. Elementy składni języka SQL służące do implementacji stronicowania nie są objęte odpowiednim standardem, tak więc zapytanie pobierające określony fragment normalnego wyniku może mieć różną postać w zależności od użytego systemu zarządzania bazą danych. Zapytanie SQL pobierające drugą dziesiątkę zamówień (z tabeli ORDER) posortowanych po dacie złożenia zamówienia (kolumna PLACED_DATE), zgodne ze składnią akceptowaną przez bazy danych MySQL i PostgreSQL wyglądałoby następująco:

SELECT * FROM ORDER
ORDER BY PLACED_DATE
LIMIT 10 OFFSET 10

Apache iBATIS Data Mapper

Technologia iBATIS Data Mapper nie generuje zapytań SQL a jedynie uruchamia te zdefiniowane przez programistę. Implementując stronicowanie trzeba więc posługiwać się takimi samymi metodami jak w przypadku użycia JDBC. Ograniczanie zakresu wyniku zapytania musi być zaimplementowane w zapytaniu SQL, w sposób właściwy dla używanej bazy danych.

21 października 2007

Narzędzie 'Red Hat Palette' z Red Hat Developer Studio

W dzisiejszym artykule pokażę jedno z narzędzi Red Hat Developer Studio, panel listujący biblioteki znaczników JSP (także dla JSF) o nazwie Red Hat Palette. Dobry obrazek wart jest tysiąca słów, zacznę więc od pokazania jak ten panel wygląda. Oto on:



I po co nam taki panel? Jest to podręczna lista tego, co mamy do dyspozycji – bardzo przydatna dla tych, którzy nie koniecznie pracują z JSP/JSF na co dzień i nie pamiętają nazw odpowiednich znaczników. Szybkie spojrzenie na taką listę dobrze odświeża pamięć. Panel ten obsługuje także funkcję drag-and-drop. Po przeciągnięciu elementu z panelu na kod źródłowy strony automatycznie dodawana jest deklaracja odpowiedniej przestrzeni nazw oraz pojawia się okno do wprowadzenia wartości atrybutów. Aby panel był wyświetlany należy, jak to w Eclipse, wybrać opcje Window > Show View > Other > Red Hat Palette i nacisnąć OK. Widać to na poniższej ilustracji:



Do panelu można dodawać również swoje biblioteki. W tym celu należy użyć funkcji importu klikając ikonę wskazaną na poniższej ilustracji:



Red Hat Developer Studio standardowo ma już zdefiniowane wiele bibliotek. Dla przykładu MyFaces Tomahawk. Aby skonfigurować, które biblioteki będą pokazane w palecie należy kliknąć na ikonę konfiguracji wskazaną na poniższej ilustracji:



Biblioteki podzielone są na grupy. Biblioteka Tomahawk znajduje się w grupie MyFaces. Aby biblioteka ta była widoczna w palecie należy dla parametru 'hidden' grupy MyFaces wybrać wartość 'no'. Pokazano to na poniższej ilustracji:



Grupa MyFaces oprócz Tomahawk zawiera także biblioteki Extensions i Sandbox. Domyślnie mają one parametry 'hidden' ustawione na 'no' a więc wszystkie będą widoczne w palecie. Paleta wygląda teraz tak:


16 października 2007

Integracja technologii Facelets i Ajax4JSF

Za punkt wyjścia do dzisiejszego artykułu przyjmę, że czytelnik ma do dyspozycji jakąś działającą aplikację JSF 1.2 + Facelets, do której będzie można dołożyć Ajax4JSF i przetestować poprawność integracji. Jeśli takiej aplikacji akurat nie ma pod ręką, to można ją sobie bardzo łatwo wygenerować używając Red Hat Developer Studio, o którym pisałem w artykule "Już jest Red Hat Developer Studio". Tą właśnie aplikacją, wygenerowaną przy użyciu IDE od Red Hat’a będę się posługiwał w treści artykułu. Jak wygenerować taką przykładową aplikację JSF 1.2 + Facelets pokazałem przy okazji artykułu "Integracja JSF 1.2 i Spring z pomocą Red Hat Developer Studio".

Zaczynamy od pobrania biblioteki Ajax4JSF. Ja pobrałem najnowszą w chwili pisania tego artykułu wersję 1.1.1 (plik jboss-ajax4jsf-1.1.1-bin.zip). Archiwum rozpakowujemy i kopiujemy plik \ajax4jsf\lib\ajax4jsf-1.1.1.jar do folderu \WEB-INF\lib\ naszej aplikacji. Kolejny krok to dodanie poniższej konfiguracji do pliku web.xml:

<context-param>
<param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
<param-value>com.sun.facelets.FaceletViewHandler</param-value>
</context-param>
<filter>
<display-name>Ajax4jsf Filter</display-name>
<filter-name>ajax4jsf</filter-name>
<filter-class>org.ajax4jsf.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>ajax4jsf</filter-name>
<servlet-name>Faces Servlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

Następnie, z pliku faces-config.xml usuwamy deklarację mechanizmu obsługi widoku technologii Facelets, jako że teraz odpowiednia konfiguracja będzie wykonana przez Ajax4JSF, usuwamy więc element:

<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>

To by było na tyle, jeszcze tylko przekonamy się, że działa. Edytujmy dowolną stronę naszej aplikacji zawierającą element wprowadzania tekstu, tj. h:inputText (albo input z atrybutem jsfc="h:inputText"). Jeśli używamy aplikacji pochodzącej z Red Hat Developer Studio to będzie to plik \pages\inputname.xhtml. Do wybranego elementu h:inputText (lub input) dodajmy podelement a4j:suport, tak jak pokazano poniżej, dodajemy także deklarację przestrzeni nazw a4j:

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="https://ajax4jsf.dev.java.net/ajax">

<input jsfc="h:inputText" id="name" value="#{person.name}">
<a4j:support event="onkeyup" reRender="personName" />
</input>

Atrybut event elementu a4j:support określa zdarzenie, które spowoduje wysłanie żądania AJAX do serwera, uaktualniając tym samym wartość właściwości name elementu zarządzanego JSF o nazwie person. Atrybut reRender określa, że każdorazowo po otrzymaniu odpowiedzi na wysłane żądanie AJAX zostanie odświeżony element strony z atrybutem id="personName". Więcej o samym Ajax4JSF można przeczytać w dokumencie "Ajax4jsf Developer Guide". Dodajmy więc na naszej stronie (zaraz pod elementem form) element do wyświetlania wartości #{person.name} z atrybutem id="personName", tj:

<h:outputText value="Wpisano: #{person.name}" id="personName"/>

Uruchommy teraz aplikację i przekonajmy się, że działa. Każde naciśnięcie (a właściwie zwolnienie) klawisza na klawiaturze powinno być odzwierciedlone w tekście wyświetlanym przez element h:outputText. U mnie wygląda to tak:

15 października 2007

Terminologia ze świata urządzeń mobilnych

Od kilku dni jestem właścicielem palmtopa E-TEN Glofiish X500+ z systemem Windows Mobile 6 Professional. Ten oto fakt sprawił, że zacząłem się żywo interesować światem tego typu urządzeń a w szczególności ich programowaniem, najlepiej w Javie. Moja wiedza na temat tego segmentu jest znikoma, postanowiłem więc zacząć od opanowania zalewu terminologii, od ustalenia co jest co. Poniżej zademonstruje moje zrozumienie, wynikające w dużej mierze z lektury Wikipedii. Komentarze mile widziane.

Pierwsza kategoria nazw dotyczy klasyfikacji samych urządzeń. Do tej kategorii zaliczam terminy: PDA (akr. Personal Digital Assistant), palmtop, smartphone oraz MDA (akr. Mobile Digital Assistant). PDA i palmtop oznaczają dokładnie to samo, czyli mały komputer mieszczący się w dłoni. Słowo "palm" z angielskiego oznacza "dłoń". Dla odróżnienia, laptop to komputer trzymany na kolanach a desktop to ten, który stoi na biurku. MDA miało być nazwą dla PDA rozszerzonego o moduł GSM, tak więc MDA to PDA z funkcją telefonu komórkowego. Nie słyszałem jednak żeby ktoś używał tego terminu, chyba nie jest on zbyt popularny, więc dla uniknięcia zamieszania swojego Glofiish’a nazywał będę palmtopem. Smartphone to coś, co jest głównie telefonem, a więc w szczególności ma ograniczony rozmiar wyświetlacza i dodatkowo wyświetlacz ten nie jest dotykowy. Oczywiście granica między palmtopem a smartphonem oraz zwyczajnym telefonem jest płynna i chyba też nie koniecznie jest to ważne żeby ją jakoś ściśle zdefiniować.

Druga kategoria nazw wiąże się z systemem operacyjnym zainstalowanym na danym urządzeniu, ale nie jest to nazwa samego systemu. Do tej kategorii zaliczam terminy: Pocket PC i Palm. Pocket PC to palmtop (lub MDA) z systemem Windows w dowolnej wersji. Termin ten zapewne pochodzi od nazw pierwszych wersji systemu Windows na palmtopy. Były to Pocket PC 2000 i Pocket PC 2002. Palm to palmtop z systemem operacyjnym Palm OS.

Trzecia kategoria to różne nazwy opisujące system operacyjny. Do tej kategorii zaliczam terminy: Windows CE, Windows Mobile, Pocket PC, Symbian OS (oraz po prostu Symbian) i Palm OS. Z Palm OS i Symbian OS sprawa jest prosta. Są to nazwy systemów operacyjnych. Ten drugi stosowany jest w ogromnej ilości smartphone’ów, w szczególności, w bodaj wszystkich produkowanych przez Nokie. Windows CE to taki skrót dla określenia wszystkich systemów Windows opartych na jądrze CE. CE to właśnie jądro dla systemów Microsoft’u na palmtopy, tak jak NT jest jądrem dla Windows XP i innych przeznaczonych dla komputerów klasy PC. Sortując chronologicznie, kolejne wersje systemów z jądrem CE to: Pocket PC 2000, Pocket PC 2002, Windows Mobile 2003, Windows Mobile 2003 SE (akr. Second Edition), Windows Mobile 5 i najnowszy Windows Mobile 6. Do tego dochodzą jeszcze różne edycje. Dla najnowszej wersji są to Classic (dla palmtopów bez telefonu), Professional (dla palmtopów z telefonem) oraz Standard (dla smartphone’ów).

Ostatnia kategoria to terminy związane z platformą Java dla urządzeń mobilnych, są to: J2ME, Java ME, CDC, CLDC i MIDP. Jest to sedno moich zainteresowań i z pewnością coś na ten temat będę jeszcze pisał. Póki co sam wiem nie wiele więc się powstrzymam.

3 października 2007

Aliasy dla nazw importowanych klas

Jakiś czas temu przyszedł mi do głowy bardzo prosty i zdawałoby się genialny w swej prostocie pomysł, aby umożliwić nadawanie aliasów importowanym klasom. Wyobraźmy sobie kod, który jednocześnie używa wielu klas o tej samej nazwie a pochodzących z różnych pakietów. Przykładowo java.sql.Date i java.util.Date. Póki co nie ma wyjścia – musimy używać w pełni kwalifikowanych nazw klas. Rozwiązanie problemu wydaje się oczywiste – wystarczyło by udostępnić składnie zademonstrowaną poniżej lub jakąś analogiczną:

import java.util.Date;
import java.sql.Date as SqlDate;

Znaczenie takiej konstrukcji jest chyba oczywiste. Co więcej, takie rozszerzenie języka jest bardzo łatwe do zaimplementowania, jako że jest to tylko lukier syntaktyczny (składniowy). Pomysł ten powrócił do mnie dzisiaj i tym razem zadałem sobie trud sprawdzenia, co Internet ma do powiedzenia w tej sprawie. Okazuje się, że pomysł mój jest całkiem rozsądny, jako że ten sam pomysł mieli też inni ludzie i co więcej zarejestrowali go jako propozycję rozszerzenia (ang. request for improvement) języka Java. Problem był zgłaszany kilkukrotnie, ale najbliżej tego, co powyżej przedstawiłem jest zgłoszenie w BugDatabase numer 4194542. Skoro pomysł jest rozsądny i łatwy w implementacji to czemu jeszcze tego nie mamy? Po lekturze komentarzy pod propozycjami wprowadzenia tego lub podobnego rozszerzenia mogę wskazać dwa kontrargumenty. Pierwszy natury praktycznej – te same klasy mogły by mieć różne nazwy w różnych fragmentach kodu, jako że nic nie mogłoby powstrzymać nas od nadania tej samej klasie różnych aliasów w zależności od sytuacji. Jest to niewątpliwie kontrargument wart rozważenia. Drugi to już bardziej filozofia – należy rozważnie wprowadzać nowe elementy składni, aby język nie rozrósł się zanadto. Język Java ma już jednak w tej chwili kilka konstrukcji składniowych, które można by, kierując się tym tokiem rozumowania, uznać za zbędne. W wersji 5 (tj. 1.5) dodano kilka kolejnych. Przykładem niech będzie instrukcja warunkowa (dostępna bodaj od początku), np.:

return yourAge() > 50 ? "An old man" : "A young person";

Z konstrukcji tej można by przecież było zrezygnować, a powyższy kod zastąpić kodem używającym konstrukcji warunku:

if( yourAge() > 50 )
return "An old man";
else
return "A young person";

Jestem ciekaw, jakie są opinie polskich programistów na ten temat. Jesteście "za a nawet przeciw"? Widzicie jeszcze jakieś kontrargumenty?