Mały przegląd ORM'ów dla Rubiego
Posted by Jarosław Zabiełło Wed, 07 Nov 2007 23:16:00 GMT
Wcześniej pisałem o dostępnych bibliotekach implementujących warstwę Widoku z modelu MVC dla Ruby on Rails. Teraz krótko o ORM’ach. Jest ich już kilka.
Active Record – podstawowy ORM używany we frameworku Ruby on Rails. Dosyć prosty, ma ładne walidacje, wywołania zwrotne (callback), obsługuje relacje między tabelami. Wadą jest stosunkowo słaba implementacja. Kod jest wciąż mało zoptymalizowany wydajnościowo i pod kątem zużycia pamięci. Brakuje wciąż parametryzowanych wywołań SQL’a, iteratorów zamiast zwracania listy obiektów, próba aktualizacji jednej kolumny w tabeli powoduje aktualizację wszystkich pól rekordu. Użycie LEFT JOIN’ów zapomocą :include powoduje że nie działa :select (zwracane są wszystkie pola). Moim zdaniem, jest tu sporo do poprawienia.
Sequel – lekki ORM przypominający składnikowo ten, stosowany w Django. W efekcie jest trochę bardziej obiektowy niż Active Record. Mniej brutalnych wstawek czystego SQL’a jak to jest w Active Record. Zostawiam sobie go na póżniej do zbadania. Możliwy konkurent dla Data Mappera (o którym niżej)
OG – ORM używany w alternatywnym do Railsów frameworku Rubiego – Nitro. Nie słyszałem aby ktoś używał go poza tym frameworkiem. Podobno są zwolennicy Nitro. Ale na razie nie spotkałem się z przekonującymi argumentami o wyższości Nitro nad Rails.
RBatis – port javowego iBatis napisany w Ruby. Teoretycznie wepnie się w dowolną strukturę, bo jest oparty na czystym SQL. Tzn. pozwala na odpowiednie zamapowanie tabel i ich pól na obiekty Rubiego. Co ciekawe, pozwala ominąć niewygodne nazwy pól tak, aby z poziomu obiektu Rubiego używać innej nazwy kolumny niż występuje w tabeli. Trudno mi coś powiedzieć o dojrzałości tego projektu, ale chyba rozwija się dosyć powoli. Trochę mnie odepchnęła informacja o tym, że wciąż nie ma zaimplementowanych joinów.
Data Mapper – na razie chyba najbardziej obiecujący ORM ktory można wpiąć w Rails zamiast/obok Active Record. Jest dużo szybszy, bo lepiej zoptymalizowano budowanie zapytań. Posiada ładniejszą, bardziej obiektową składnię niż Active Record. Zresztą sami zobaczcie:
Zoo.all(:name => 'Dallas')
# odpowiednik dla
Zoo.find(:all, :conditions => ['name = ?', 'Dallas']).
# 'gt' znaczy większy-niż. Istnieje również 'lt' (mniejszy-niż)
Person.all(:age.gt => 30)
# 'gte' znaczy większy-lub-równy-niż. Jest też 'lte'.
Person.all(:age.gte => 30)
Person.all(:name.not => 'bob')
# Jeśli wartością w parze jest Arar, to generowany jest sql'owy warunek IN()
Person.all(:name.like => 'S%', :id => [1, 2, 3, 4, 5])
# To samo co Zoo.first(...)
Zoo[:name => 'Ft. Worth']
# To samo co Zoo.find(11)
Zoo[11]
# Przykład zwrotu NOT IN ().
Person.all(:name.not => ['bob','rick','steve'])Poza tym Data Mapper jest wielowątkowy i domyślnie używa LEFT JOIN’ów w wypadku użycia relacji. Przeciwnie do Active Record, który nie jest odporny na wątki i wymaga jawnego użycia parametru :include aby włączył joiny (przy okazji likwidując działanie parametru :select).
(UPDATED 2007-12-16) Pod JRuby można odpalać Active Record JDBC (zobacz listę gemów: gem search -r ActiveRecord) oraz Active Hibernate.


Kanały IRC![[Dilber w Onecie]](/images/larry.png)


dżiz, dobre pare sekund sie zastanawiałem co to do cholery ten Arar :D
Faktycznie AR ma sporo wad, ciekawe dlaczego w 2.0 praktycznie nic nie poprawili, a jakby nie patrzeć komunikacja z baza jest dość ważnym elementem frameworka. Może czas rzucić okiem na Data Mappera.
Nie poprawili bo Rails 2.0 to PR dla tego calego RESTfulu, Rails 2.0 to glownie rozwoj calego ActionPack’a, a szkoda. Trzeba bedzie sie zastanowic nad tym DataMapperem, wyglada obiecujaco. Ale to juz chyba tylko w nowych projektach :( stare zabardzo utkiwly w ActiveRecordzie
Aha, Jarku, jak to wyglada w przypadku DataMappera gdy zapisujemy cos do bazy ? Tak samo jak w AR ? wszystkie kolumny sa odswiezane ?
Kolejna kwestia to to czy jak zrobie relacje typu has_many, belongs_to i wywolam obiekt_a.obiekt_b.obiekt_a to zostana wywolane 2 zapytania dla obiektu_a ? czy obiekt_b bedzie posiadal referencje obiektu_a ?
No i czy tak jak w AR trzeba pilnowac sie przy wiekszych relacjach i uzywac self.reload po zmianiejakiegos foreign_key’a dla obiektu, tak aby wywolanie relacji bylo zsynchronizowane ze zmiana klucza/jego usunieciem ? Przyklad:
obiekt_a = Obiekt_a.find(:first, :include => [:obiekt_b]) obiekt_a.obiekt_b_id = nil puts obiekt_a.obiekt_b # zwroci obiekt_b obiekt_a.reload puts obiekt_a.obiekt_b # zwroci nil
Ale kaszana :( dobra tu jest kod z poprzedniego wpisu http://pastie.caboo.se/private/xntb4gs37nlzc10jbew
http://pastie.caboo.se/pastes/109878 Takie male porownanie wydajnosci, znalecione na pastie. Wyglada na to ze DM nie zawsze jest szybszy od AR
No calkiem fajny ten DataMapper. Nie da sie ukryć. Czesc Pawel ;]
I tak sqlalchemy rządzi :)
climbus: SA może i rządzi ale w innym miejcu, nie w Ruby.
Paweł Kondzior: DM jest w niektórych miejscach wolniejszy, piszą o tym na stronie, wciąż trwają prace nad optymalizacją kodu. Z tego co piszą, zmiana zawartości kolumny nie pociąga konieczności zmian wszystkich kolumn w rekordzie tak jak w AR. Z tymi relacjami, to jest opcjonalny parametr :lazy. Jeśli jest true, wtedy pole takie odpala dodatkową kwerendę w momencie próby dostania się do niego. Jeśli jest false, to nic nie jest wykonywane, bo DM domyślnie włącza LEFT JOIN’y jeśli są relacje. W AR trzeba to jawnie zapodać za pomocą :select. Tu jest to domyślnie włączone.
Może to nie orm ale na najbardziej wypasione zapytania pozwala Ambition: http://errtheblog.com/post/10722. Ambition jeździ bezpośrednio po AST i nie wymaga aby konstrukcje miały “semantyczny” sens w Rubym.
Z tego co pamiętam aktualizowanie wszystkich pól podczas update’u w AR jest “by design”. Chodzi o to, żeby dwa różne wątki aplikacji, update’ując pojedyńcze pola nie spowodowały, że w zapisany w bazie danych rekord nie będzie spełniał reguł walidacji.
Poza tym wydaje mi się, że update pojedyńczego pola a update wszystkich pól to nie jest duża różnica dla bazy. Ale nigdy nie próbowałem tego zmierzyć, więc może się mylę.
Najwyższa pora porzucić ORM-y. Społeczność powinna skoncentrować się na stworzeniu produkcyjnej, czysto obiektowej bazy danych dla Rubiego. Odpowiednika Pythonowego ZODB. Wierzę, że na tym będzie polegała następna rewolucja we frameworkach – masowe wykorzystanie obiektowych baz danych w małych i średnich aplikacjach webowych.
qertoip: a wiesz, że może i masz rację? :) Planowałem jedną aplikację napisać w Pylons i SQLAlchemy ale być może napiszę ją w Pylons i… ZODB. BTW, poza ZODB jest jeszcze jedna (też w Pythonie) – Durus. Nie używałem, ale chyba jest prostsza. Acha, i jest przecież, napisana w Javie – DB4O Object Database. Może to by poszło na JRuby?
Pójdzie, tylko przed kolejną wersją kompilatora trzeba korzystać z POJO (chyba że IRubyObject rozszerzałby Serializable)
TestObject to jest klasa javowa z polem String:
yyy, wielowątkowy? czy to znaczy, że wreszcie będę mógł przyspieszyć działanie moich aplikacji na moim Core 2 Duo T7200 co to dwa rdzenie ma? ;D
Może odrobinę off topicowo, ale powstaje nowy framework dla rubiego: Merb http://merbivore.com/
Pachnie mi odpowiednikiem pylonsa w ruby. Pozwala na bardzo łatwo zmianę pomiędzy ORM’a, bibliotek JS czy też silnika templetów
czesc
W komentarzach tego artykulu jest kilka informacji (testy) o bazie Durus fastest python database interface
pozdrawiam