25 kwietnia 2007

Co to jest JDO i co nowego w JDO 2.0

JDO, czyli Java Data Objects to standard rozwijany w ramach JCP (akr. Java Community Process). Pierwotna wersja specyfikacji, JDO 1.0, powstała w wyniku prac nad JSR 12 (akr. Java Specification Request) i oparta była o standard ODMG 3.0 (akr. Object Data Management Group). Jej finalna wersja została zatwierdzona i opublikowana w kwietniu 2002. Aktualną wersję JDO, JDO 2.0, opracowano w ramach JSR 243 i zatwierdzono ostatecznie, po długotrwałych trudnościach natury politycznej, w maju 2006.

JDO jest standardem interfejsu programistycznego (ang. API) dla technologii trwałości danych. Główne idee, jakie stara się realizować to pełna niezależność od rodzaju źródła danych oraz przeźroczystość. Niezależność od rodzaju źródła danych oznacza, że aplikacja używająca JDO może być bez żadnych zmian w kodzie uruchamiana na relacyjnej, obiektowej oraz każdej innej bazie danych, a także na źródłach danych, których zwyczajowo nie nazywa się bazami danych. Przeźroczystość przejawia się w kilku aspektach, najważniejsze z nich to:
  • Trwałość przez osiągalność (ang. persistence by reachability) – utrwalając obiekt, który ma referencje do innych obiektów obiekty osiągalne z obiektu utrwalanego explicite zostaną także utrwalone. Jeśli np. nasza klasa Osoba ma referencję nazwaną adresZamieszkania do obiektu typu Adres to utrwalając obiekt klasy Osoba, powiązany z nim obiekt klasy Adres zostanie także automatycznie utrwalony.
  • Przeźroczysta nawigacja – pobierając pewien obiekt ze źródła danych nie pobieramy wszystkich obiektów, które są osiągalne z danego obiektu poprzez referencje. Wyobraźmy sobie obiekt klasy Sprzedawca, który ma referencje do wszystkich obsługiwanych przez siebie obiektów Klientów, którzy z kolei mają referencje do wszystkich swoich obiektów Zamówień, te mają dalsze referencje do obiektów klasy Towar. Byłoby to tysiące lub dowolnie dużo więcej obiektów do pobrania, podczas gdy my chcemy znać tylko imię i nazwisko danego sprzedawcy. Przeźroczysta nawigacja oznacza, że efektywnie pobierany jest tylko obiekt Sprzedawca, zaś dopiero odwołanie się do jego referencji powoduje rzeczywiste, ale zupełnie nie widoczne dla programisty, pobranie obiektu referowanego. Programujemy więc tak jakby wszystkie obiekty były pobrane od razu, ale rzeczywisty odczyt ze źródła danych następuje dopiero wtedy gdy jest to konieczne. Funkcjonalność ta dostępna jest jednak jedynie wtedy, gdy kod wykonuje się w kontekście zarządzanym przez managera JDO.
JDO jest jednak tylko standardem definiującym interfejs programistyczny. Aby mieć z niego jakikolwiek rzeczywisty pożytek niezbędna jest jeszcze implementacja. JDO realizuje ideę niezależności od rodzaju źródła danych tylko w tym sensie, że nie narzuca mechanizmów charakterystycznych dla np. relacyjnych baz danych, rzeczywista niezależność zależy od faktycznego istnienia odpowiedniej implementacji. Referencyjną implementacją dla JDO jest JPOX, http://www.jpox.org/. JPOX 1.0 implementuje JDO 1.0 dla relacyjnych baz danych, JPOX 1.1 to już JDO 2.0 ale również tylko dla relacyjnych baz danych. JDO 2.0 jest w pełni wstecz kompatybilne tak więc implementacja JDO 2.0 jest również implementacją JDO 1.0. JPOX 1.2 dodaje wsparcie dla obiektowej bazy danych DB4O, choć w chwili pisania tego artykułu nie ma jeszcze wersji stabilnej, najnowszą jest JPOX 1.2.0-beta-2. Spis dostępnych implementacji, choć nie wiem czy kompletny, można znaleźć na stronie http://db.apache.org/jdo/impls.html.

Jeśli mamy już w ręce pewien obiekt pobrany ze źródła danych, to kolejne obiekty pobieramy w sposób przeźroczysty, nie widoczny dla programisty, poprzez odwołanie się do referencji tego obiektu w zwykły, charakterystyczny dla Javy a nie dla JDO sposób. Jednak musimy jakoś pobrać ten pierwszy obiekt. Nie owijając w bawełnę. Praca z danymi wymaga istnienia dobrego mechanizmu wyszukiwania. JDO definiuje swój własny język zapytań, JDOQL. Zanim jednak popadniemy w zniechęcenie... JDOQL jest prosty, intuicyjny i... podobny do Javy. Tak! Do Javy, nie do SQL! Począwszy od JDO 2.0 JDOQL został rozszerzony w ten sposób, że można go używać także w bardziej SQL’owy sposób, ale nie trzeba, a udostępniono tą możliwość dlatego, że niektórzy tak wolą, oraz pewnie po to, żeby upodobnić JDO do JPA, ale to inna historia. Stworzenie prostego zapytania mogłoby wyglądać tak:

Query query = pManager.newQuery(Osoba.class, „wiek >= 50 && wiek < 60");

Jak duża jest różnica między JDO 1.0 a JDO 2.0? Ogromna! A właściwie to nie jest to różnica tylko różnice. Niektóre, istotne rozszerzenia, jakie wprowadza JDO 2.0 to:
  • Standaryzacja mapowania obiektowo-relacyjnego – mimo że JDO nie jest w żaden sposób związane z relacyjnymi bazami danych, to w obliczu przytłaczającej dominacji RDBMS’ów jako źródeł danych zdecydowano się na standaryzację mapowania obiektowo-relacyjnego. Mapowanie to definiujemy w pliku XML, no i oczywiście nie musimy tego robić jeśli nie będziemy uruchamiać aplikacji na relacyjnej bazie danych.
  • Rozszerzenie mechanizmu uruchamiania zapytań i języka zapytań JDOQL – liczne usprawnienia, w szczególności, umożliwienie wykonywania zapytań agregujących, np. zliczających liczbę obiektów lub wyliczających sumy, minima czy maksima. Dodanie mechanizmu projekcji, tak by możliwe było pobieranie tylko wybranych atrybutów obiektów, w tym możliwość projekcji do obiektów dowolnego typu. Dodanie mechanizmu stronicowania wyników zapytań.
  • Mechanizm odłączania i przyłączania do kontekstu zarządzanego JDO – wydaje się to być niezbędne dla sprawnej implementacji nowoczesnych aplikacji wielowarstwowych i trudno mi jest wyobrazić sobie, że mogło tego pierwotnie nie być. Obiekt pozyskany ze źródła danych można teraz wyłączyć z kontekstu zarządzania JDO, zmodyfikować w innej warstwie i ponownie przyłączyć, co spowoduje zapisanie jego zmodyfikowanego stanu do bazy danych.
  • Mechanizm definiowania zachłannego pobierania obiektów – mechanizm, który pozwala na zdefiniowanie, które obiekty powiązane referencjami z pobieranym obiektem zostaną pobrane w sposób zachłanny (ang. eager). Np. jeśli obiekt klasy Osoba posiada referencję do obiektu klasy Adres to możemy określić aby pobierając obiekt klasy Osoba pobrać od razu skojarzony z nim adres.
  • Interfejsy zarządzania pamięcią podręczną drugiego poziomu – ze względów wydajnościowych wiele implementacji JDO 1.0 dostarczało pamięć podręczną (ang. cache) drugiego poziomu, tj. współdzieloną przez wszystkie obiekty managerów JDO. JDO 2.0 standaryzuje interfejs programistyczny do zarządzania tą pamięcią podręczną, choć specyfikacja dopuszcza brak rzeczywistej implementacji, tj. implementację w postaci zaślepki.

5 komentarzy:

raflik pisze...

Fajny artykuł, ale mnie np. interesuje, czym JDO właściwie różni się od JPA (w założeniach, zastosowaniach, ...)?

Mariusz Lipiński pisze...

Hmmm... nie ma lepszej metody porównania, jak tylko poznanie obydwu technologii. Z pewnością jeszcze nie raz będę pisał o JDO, JPA i innych technologiach trwałości danych... ale trochę potrwa zanim zabiorę się za porównania. Póki co mogę polecić ci lekturę artykułu JDO vs JPA zamieszczonego na stronach JPOX'a.

blochoo pisze...

Mam takie pytanie odnosnie pobierania typu eager:
Napisales o tym, ze mozna JDO skonfigurowac tak, aby pobierajac obiekt Osoba, majacy referencje na Adres pobrac oba obiekty na raz. Chyba nie jest tak do konca...

Jest to prawda tylko w przypadku polaczenia obiektow 1:1 majacych pola w tej samej tabeli.

Jesli probujesz pobrac obiekt, ktory ma w sobie obiekt trwaly/kolekcje obiektow trwalych (persistent capable), to nie jestes w stanie pobrac ich za jednym razem (przy pomocy jednego zapytania typu JOIN z obiektem nadrzednym). JPOX bedzie wykonywal podzapytania.

Jest to niewydajne, ale Panowie z JPOX tlumacza to tym, ze przy robieniu JOINa, nigdy nie siegalibysmy do cache'a. Moim zdaniem rozwiazanie slabe - inne systemy w tym JPA radza sobie z tym lepiej.

Krotko mowiac: EAGER fetching w wykonaniu JDO to fikcja.

Pozdrawiam,
blochoo
CS@PP

Mariusz Lipiński pisze...

Dzięki za komentarz. Nie ma nic bardziej kształcącego niż dyskusja!

Co do automatycznego pobierania obiektów referowanych; mając na uwadze specyfikę JDO trudno jest w ogóle mówić o uruchamianiu zapytań. Rozumiem, że piszesz o przypadku użycia, kiedy to JDO używamy w roli ORM, ja pisałem o JDO w ogólności. No ale wracając do problemu o którym piszesz; przychylam się po trosze zarówno do Twojej opinii jak i do opinii cytowanych przez Ciebie developerów JPOX'a. Jasne, że pobierając obiekty, których nie ma w średnim przypadku w pamięci podręcznej wydajniej jest uruchomić jedno zapytanie pobierające wszystkie niezbędne dane, ale równie często jak pomocne, byłoby to zabójcze. Wystarczy wyobrazić sobie system obsługi zamówień jakiegoś sklepu internetowego. Czy naprawdę chciałbyś, aby za każdym razem, kiedy operator pobiera zamówienie odpalane było gigantyczne zapytanie pobierające oprócz obiektu zamówienia także obiekty produktów? Ja bym wolał żeby obiekty produktów zostały pobrane z pamięci podręcznej tym bardziej, że najpewniej zawierają one liczne referencje do dalszych obiektów. No ale to są dywagacje oderwane trochę od technologii.

Co do JPA. Czym innym jest pobranie drzewa obiektów zapytaniem JPQL z użyciem JOIN FETCH a czym innym z wykorzystaniem mechanizmu adnotacji FetchType.EAGER. Z moich obserwacji poczynionych na TopLink Essentials wynika, że w tym drugim wypadku uruchamiane są właśnie oddzielne zapytania do pobrania każdego z obiektów, no chyba że dany obiekt jest już w pamięci podręcznej. Zapytanie z użyciem JOIN FETCH rzeczywiście powoduje uruchomienie jednego super SQL’a ale pamięć podręczna jest ignorowana.

Rozumiem, że problem na który zwracasz uwagę jest taki, że w JDO nie ma odpowiednika JOIN FETCH z JPA. Jeśli masz rację to rzeczywiście punkt dla JPA, choć nie uważam że JPA rozwiązuje w ten sposób problem pod tytułem jedno wielkie zapytanie kontra wiele małych plus pamięć podręczna. Sądzę, że takie a nie inne działanie jest raczej dziełem przypadku. Zwróć też uwagę, że JOIN FETCH możesz użyć tylko raz w zapytaniu. Technologia iBATIS SQL Maps nie ma takich ograniczeń. Przykład podałem w artykule Niebanalny przykład mapowania dla Apache iBATIS Data Mapper
. Czy oznacza to automatycznie że iBATIS SQL Maps to technologia lepsza? Raczej nie.

Anonimowy pisze...

huehuehue ale z ciebie andrzej cebulak