Friday, July 4, 2008

Iluzja O/RM

Gdy po raz pierwszy dałem się uwieść wspaniałemu konceptowi O/RM, Hibernate był w wersji 2 z kawałkiem, a o JPA nawet nie słyszałem.
Idea jest naprawdę wspaniała: raz mapujesz pomiędzy tabelami a obiektami, ba (!) nawet framework stworzy schemat bazy danych za ciebie, a potem już tylko pracujesz z obiektami - programista obiektowy swoją drogą, bazodanowiec swoją. Prawdziwy raj!

We wspomnianej wersji Hibernate'a pałętało się sporo plików xml, ale wspomagając się xdocletem można było jakoś przeżyć. Weszły anotacje i miało być jeszcze prościej, wspanialej - no, słowem obiektowa orgia.

Moim zdaniem wcale nie jest tak kolorowo. Po pierwsze dlatego, że nie możliwe jest zmapowanie O/R raz na zawsze. Baza danych wciąż o sobie przypomina. A to wyskoczy jakiś constraint i trzeba pogrzebać w bazie w poszukiwaniu przyczyn (a potem okazuje się, że to jakiś błąd mapowania:]), a to model obiektowy ulega refaktoringowi albo rozbudowie i znów należy wracać do bazy danych - wciąż wracamy do bazy danych stojąc okrakiem pomiędzy obiektami i relacjami.

Kolejną kwestią jest to, że cała idea O/RM skłania do zaniedbywania bazy danych (myślimy - nie jest moją rzeczą zajmować się bazą danych, jestem programistą obiektowym...). W związku z czym baza jest projektowana niechlujne lub (o zgrozo!) pozwala się frameworkom na zarządzanie schematem. Prowadzi to tak wielkiej kaszany w bazie danych, jak wielka kaszana tylko może być: każdy obiekt ma swoją tabele, istnieje wiele tabel zawierających po kilka wierszy ("a, bo mnie tak wyszło z mapowania..."), tabele z jedną kolumną są na porządku dziennym.

Do czego to prowadzi? Ano, bywa, że O/RM mają problem z wydajnością i czasem korzystnie jest wspomóc się jakimiś procedurami składowanymi i widokami. W takim momencie okazuje się, że praca ze schematem zarządzanym przez O/R M to gorzej niż czyściec dla duszy programisty.

W pewnym momencie rozwoju J(2)EE było paru ludzi (żeby nie wymieniać nazwisk), którzy pchnęli dalszy rozwój technologii w określonym kierunku. A społeczność za tym poszła, bo wizja była fajna. Aktualnie mam na myśli idę O/RM i całą resztę kryjąca się za wzorcem Domain Store. A przecież istnieją rozwiązania, które społeczności Javy zostały zignorowane, a które świetnie się sprawdzają w innych technologiach. Mówię rozwiązaniach, które warstwę danych organizują w okół bazy danych, a nie w okół modelu obiektowego, np: Oracle BC4J albo implementacja Active Record w Ruby on Rails

O faworyzowaniu pewnych rozwiązań świadczy choćby fakt, że do tej pory nie doczekaliśmy się popularnej i w pełni funkcjonalnej implementacji Active Record dla Javy. Myślę, że mogło by to wnieść wiele dobrego do aplikacji, które rozwijamy.

11 comments:

  1. Nie bardzo rozumiem dlaczego Hibernate jest gorszy/lepszy od Active Record z Rails. Przecież to dokładnie to samo.

    Active Record to też O/RM, też ma problemy z wydajnością. Różnica jaka jest to przedewszystkim zastosowanie Convention over Configuration. Dzięki temu nie trzeba pisać XML-a bądź/i annotacji. I tu zgadzam się w pełni byłoby super gdyby była taka możliwość.

    ReplyDelete
  2. Pisząc(nieco nieściśle) O/RM miałem na myśli rozwiązania typu Hibernate, JDO i inne oparte na wzorcu Domain Store
    gdzie mechanizm persystencji jest wypchnięty poza model obiektowy. Wtedy centrum twojej warstwy danych jest model obiektowy i relacje w nim zachodzące.
    W Active Record
    jest nieco inna filozofia działania. Obiekty modelu zawierają logikę, stan oraz "wiedzą" jak się zapisać do bazy danych. Tutaj warstwę danych rozpatruje się przykładając większą uwagę do bazy danych.
    W Hibernate mielibyśmy np.:
    dao.save( employee )
    a w ActiveRecord:
    employee.save()

    ReplyDelete
  3. P.S. Byłbym ostrożny w ocenianiu co lepsze, a co gorsze - myślę, że zależy od tego do czego się dane rozwiązanie stosuje.
    Pisząc posta chciałem podkreślić, że w JEE zbaczamy w jednym kierunku, gdy tym czasem są jeszcze inne możliwości, które są ignorowane.

    ReplyDelete
  4. Zgadzam się. Niestety nie mamy w javie możliwości dodawania metod w locie i innych technik metaprogramowania dostępnego w Ruby.

    Próbowano tego uprzednio zarówno w JDO jak w EJB 2.1 były możliwości zbliżone do ActiveRecords by to model się utrwalał, niestety sami wiemy ile wymagało to dodatkowej pracy.

    Stąd w java pochód technik gdzie prezystencją zajmuję się ktoś inny. Gdyby można było takiego prezystatora wMixin-ować w model.... Rozmarzyłem się.

    W sprawie oceniania jestem dalece ostrożny dlatego też używałem wyrażeń "gorszy/lepszy". Zawsze jest możliwość takiego scenariusza gdzie dobre rozwiązanie staje się złym rozwiązaniem.

    Dzięki za post i dyskusje.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Szczerze powiedziawszy nie wiem gdzie w tym Twoim przykładzie:


    W Hibernate mielibyśmy np.:
    dao.save( employee )
    a w ActiveRecord:
    employee.save()


    .... widać wyższość ActiveRecord nad np. Hibernate. Ja przynajmniej tego (jeszcze?) nie widzę.

    Po drugie:
    Od razu mówię, że nie znam się na ActiveRecord (znam się na Hibernate).
    Powiedzmy, że mamy przypadek, że chcę w jednej transakcji utrwalić w bazie 3 rekordy. Czyli w
    Hibernate będzie to coś takiego:

    session.save(employee01);
    session.save(employee02);
    // ...
    session.save(employee10);
    COMMIT;


    Hibernate (i pewnie inne ORM-y) są tutaj na tyle sprytne, że owe 10 nowych instancji zapiszą w bazie przy użycia POJEDYNCZEGO batch-inserta. Będzie to dramatycznie szybciej, niż wykonanie 10-ciu pojedynczych insertów do bazy danych.

    Piszesz, że w Active Record obiekty same wiedzą, jak się utrwalić w bazie danych. Czy są na tyle sprytne, że również potrafią się utrwalić przy pomocy takiego jednego dużego (zbiorczego) batch-inserta?
    Adam Woźniak

    ReplyDelete
  7. Co do konkretnej implementacji Active Record to nie wiem jak wewnętrznie działa.

    Wyższość konkretnego rozwiązania zależy pewnie od kontekstu. Moja myśl przewodnia była taka: w Javie koncentrujemy się tylko na modelu obiektowym danych i chcemy zapomnieć o świecie relacyjnym. Moim zdaniem nie da się uciec od baz danych. Dlatego w Javie przydała by się alternatywa w persystencji danych, która koncentrowałaby się w okół bazy danych - np. active record

    ReplyDelete
  8. Piszesz:
    "w Javie przydała by się alternatywa w persystencji danych"
    Ilość frameworków to realizująca jest i (chyba) całkiem imponująca:
    http://java-source.net/open-source/persistence

    Naliczyłem ich tam około 35 sztuk. Więc - mam wrażenie - że jednak alternatywa jednak jest (a to jedynie projekty Open Source).

    Po drugie:
    Wiele z frameworków ORM dla Java są dojrzałymi i od lat rozwijanymi projektami. Zaadresowano tam i zaimplementowano wiele właściwości, zwiększające ich wydajność (wydajność współpracy z bazą danych w szczególności).

    3)
    Piszesz:

    O faworyzowaniu pewnych rozwiązań świadczy...


    Ale niby kto to faworyzuje? Bo chyba nie programistów / projektantów masz na myśli, bo Ci wybierają (najczęściej) to co uważają za optymalne dla swojego projektu.

    Zauważę przewrotnie:
    Może nie ma owego Active Record dla Java ponieważ ... nikt tego nie potrzebują w dobie bogactwa innych silników ORM.

    W sumie, jeśli coś Tobie podpowiada, że brak Active Record dla Java jest zdumiewające, to może jest to gotowy pomysł na własny framework ;)

    JA jednak twierdzę, że zaprojektowanie mocnego i wydajnego silnika ORM jest zadaniem bardzo skomplikowanym. Dowodem na te moje słowa może być obserwacja, że dokumentacja do niektórych silników ORM ma po setki stron co przekłada się na masę właściwości w nich zaimplementowanych (cześć z nich właśnie poprawiających wydajność).

    Cytując stronę "A Brief History of TopLink":

    A great thing to happen over the past couple years is the market validation for persistence layers in general. The biggest competitor to TopLink always has been and always will be those who don't recognize the complexity of Object-Relational persistence. Those who think building their own persistence layer "looks easy enough", or feel they can "make do" with a basic persistence layer and generally assume it's an easy problem. The awareness generated by the numerous new vendors in this space, and the awareness generated by recent discussion on the POJO persistence model in EJB 3.0 have really helped validate that building your own persistence layer is a bad idea.

    ReplyDelete
  9. Ktoś kiedyś opowiedział taką historię:

    Przyjeżdżasz po pracy do domu (model z amerykańskich przedmieści, dom, ogród i garaż), zatrzymujesz się przed garażem a następnie rozbierasz cały samochód na pojedyncze elementy a następnie wkładasz każdą do garażu na swoje miejsce na półce.
    Następnego dnia rano wyciągasz wszystkie części przed garaż i składasz sobie samochód z części i jedziesz do pracy.

    ;----)))))

    W życiu można czasem pracować zdalnie, lub skorzystać z komunikacji publicznej... no ale ja jednak zapytam
    co z bazami obiektowymi? :) ktoś tego używa? Ja mam to na liście kiedyś/może do spróbowania :)

    Racjonalny Developer

    ReplyDelete
  10. Autor PATa (nakładki na Prevaylera) używał tej bazy w dość poważnych projektach.

    Osobiście przyglądam się rozwojowi db4o i myślę, że już jest na dość niezłym poziomie.

    Sądzę, że jeśli aplikacja, w której dominują operacje typu CRUD - i nie będzie (!) złożonego przetwarzania danych, to można rozważyć bazę obiektową, aczkolwiek...
    RDMS mają nieźle dopracowane mechanizmy optymalizacyjne i jeśli jesteś w potrzebie to wspomagasz się indeksami, trigerami, procedurami składowymi. W przypadku baz obiektowych jest to utrudnione.

    Pisząc aplikację stand-alone rozważałbym HSQLDB albo db4o ale teraz nie potrafię powiedzieć na co bym się zdecydował.

    ReplyDelete
  11. Skoro mowa o db4o i innych mniej lub bardziej ciekawych alternatyw dla "typowych" baz relacyjnych:
    Tak się składa, że w projektach w których biorę udział, nigdy nie stoimy przed wyborem, którego silnika bazy danych użyć, gdyż mamy to niejako narzucone, przez klienta (-ów), dla których robimy projekt. I tu nie ma co z tym dyskutować - trzeba to przyjąć do siebie i się w tym odnaleźć.
    Ale ... jest jedno "ale" ;) Mam na myśli in-memory databases. W jednym z naszych projektów korzystamy właśnie z in-memory database i przechowujemy tam dane, które z natury biznesowej, nie zmieniają się częściej niż raz na dobę.
    Ja akurat wybrałem bazę H2. Działa ona zadowalająco, nie miałem z nią dotąd żadnych problemów.
    Jeśli chodzi o wydajność nasze pobieżne testy wykazały, że operacje na tych tabelach skróciły się od kilku do kilkudziesięciu razy. A więc jest o co walczyć i jest się po schylić.

    Dlaczego wybrałem H2, a nie HSQLDB:
    Jakoś wtedy, kiedy dokonywałem tego wyboru, coś mi się w HSQLDB nie podobało - niestety nie pamiętam już co ;>

    Dlaczego wybrałem H2, a nie np. db4o:
    Tu odpowiedź jest łatwa. Miałem już sporo kodu w Hibernate jeżdżących po tych tabelach, które chciałem przenieść do in-memory database. A więc - chcąc być mało inwazyjny w kod - użyłem bazy, która wystawia normalny interfejs JDBC (czyli H2 w moim przypadku). Dzięki temu, kod Hibernate jeżdżący po tych tabelach pozostał niezmieniony (!). Należało jedynie używać połączenia z bazą H2, zamiast z bazą główną (mała zmiana kodu).

    Wciąż ubolewam, że na stronach H2, mimo, że można znaleźć wykresy rzekomo wykazujące wyższość w wydajności nad np. HSQLDB, chyba średnio jest rozwiązana wielowątkowość (patrz http://www.h2database.com/html/frame.html?features.html&main i "Multithreading Support". Kiedy to czytam mam wrażenie, że przy dużej ilości jednoczesnych wątków, H2 może okazać się mało skalowalna. Ale możliwe, że się mylę - należałoby zrobić jakieś pomiary w świecie rzeczywistym.

    Indeksy w H2:
    Za to wzruszyło mnie to, że H2 - mimo, że jest "jedynie" in-memory database - posiada indeksy. Zastosowanie u nas odpowiednich indeksów w bazie H2 dodatkowo zwiększyło wydajność.

    Pozdrowienia, Adam

    ReplyDelete