ActiveRecord vs. DataMapper

Witam,

Zaczynam pisać aplikację w merbie i zastanawiam się nad tym, którego ORMa wybrać.

Z tego co na razie zebrałem:

ActiveRecord:

  • zdaje się mieć sporo większe wsparcie - dużo pluginów do railsów, która powinny działać bez problemu i na merbie, dużo więcej użytkowników.
  • jakby nie było z ActiveRecordem znamy się od 2 czy 3 lat, nie potrzeba czasu na naukę

Datamapper:

  • chodzą pogłoski, że jest szybszy, aczkolwiek z tego co widać na benchmarkach można stwierdzić, że to tylko marketing
  • jest trochę rzeczy, które sprawiają, że niektórzy nie użyliby go w produkcji (na przykład dmilith :wink:
  • ponoć twórcy datamappera będą oficjalnie wspierać tylko jruby (nie mogę znaleźć potwierdzenia tego w sieci)
  • jest rozwijany jako oddzielna biblioteka - dla mnie to dużo lepsza opcja niż activerecord powiązany z railsami

Jeżeli ktoś ma jakieś przemyślenia na ten temat, to prosiłbym o wpis - nawet jeżeli to całkiem subiektywne niczym niepoparte opinie :wink:

Update:
No dobra, mam: http://twitter.com/jseifer/status/1013911460, ale coś mi się wydaje, że to nie jest na poważnie :stuck_out_tongue:

Jeśli rozpoczynasz nowy projekt, to Datamapper na pewno jest wart przemyślenia. Jasne, za ActiveRecord przemawia większa popularność, i fakt, że nie trzeba się niczego nowego uczyć. Za Datamapperem przemawia jego większa elastyczność:

  1. istnieją adaptery do wielu źródeł danych, nie tylko relacyjnych (jak CouchDB, LDAP, REST)
  2. system pluginów - sam DataMapper nie oferuje funkcjonalności tzw.
    dynamic finders, czyli np. find_all_by_username, ale odpowiedni plugin
    dostarcza nam tej funkcjonalności, jeśli jest ona kluczowa, albo się do niej
    przyzwycziliśmy
  3. W Datamaperze dla jednego rekordu jest tworzony tylko jedna instancja
    obiektu, np.

order = Order.find(45) order.items.first.order.object_id == order.object_id
W ActiveRecord order i order.items.order to dwa różne obiekty, w Datamapper to
jeden i ten sam obiekt. Oszczędza to pamięć, i ułatwia programowanie. W
zasadzie nie ma już potrzeby używania reload, jeśli zmiany rekordu są
dokonywane w obrębie jednej aplikacji.

Moje wrażenie jest takie, że nakład pracy, żeby przestawić się na Datamapper
jest dość ograniczony, za to otrzymujemy dużo lepszy ORM.

Dzięki za odpowiedź :slight_smile:

Zdecydowałem się na datamappera. Po przejrzeniu dokumentacji można łatwo zauważyć, że bardzo często zmiany sprowadzają się do zmiany nazw metod. Swoją drogą kompletnie nie rozumiem po co “na siłę” zmieniać nazwę metody, żeby było na przykład validates_present, zamiast validates_presence_of - żeby przypadkiem ktoś nie posądził ludzi od datamappera, że kopiują z ar? :slight_smile:

Oprócz tych rzeczy, o których piszesz jest jeszcze jedna, dość ważna w przypadku merba. Pomimo tego, że wycats i spółka tyle mówią o tym, że merb jest orm-agnostic, to niewątpliwie są pewne tendencje :slight_smile: Przeglądając aplikacje merba można łatwo zauważyć, że najwięcej z nich działa na datamapperze i to on zapewne będzie miał największe wsparcie od społeczności (chociaż z tego co widzę, to wielu twórców pluginów stara się dodawać backendy do wszystkich 3 ormów).

Jeżeli chodzi o wydajność datamappera, to muszę napisać, że wcale nie jest tak różowo jak niektórzy ludzie twierdzą. Na #rubyonrails.pl już to było kilka razy, ale na forum nikt o tym nie wspominał. Często można przeczytać, że datamapper jest szybszy niż active record. W dm-core jest skrypcik performance.rb, tutaj są wyniki dla datamappera 0.9.6 i activerecord 2.1: http://gist.github.com/10735

Zmieniłem lekko skrypt, żeby działał na postgresie i odpaliłem u siebie na nowszych wersjach, oto wyniki: http://pastie.org/320883

Jak widać przy pobieraniu wyniki są podobne, ale raczej na korzyść ar, niż dm.

Ja przymierzam się do w zamierzeniu dość dużej aplikacji w merbie i wyniki datamappera zaskakujące, wszędzie opinia, że niszczy ar, chłopaki jednak z railsów nie próżnują jak widać.

Nie wiem czy zetknąłeś się z Sequelem ( http://sequel.rubyforge.org/ ). Jest to dość ciekawy ORM, dość elastyczny gdyż wykorzystuje pełne możliwości Rubiego (DSL oparty na przeładowanych operatorach). Swego czasu pisał o nim na swoim blogu Jarek Zabiełło. Myślę, że warto wziąć go także pod uwagę. Tutaj masz krótką prezentację na temat użycia Merba z Sequelem: http://loriholden.com/merbcamp2008/index.html

Sequel sam w sobie to tylko DB Persistence Layer, tak tez byl towrzony i wersja 1.0 nie miala praktycznie zadnej wartstwy do tworzenia obiektow i relacji pomiedzy nimi. Od jakiegos czasu jest Sequel::Model zbudowany na Sequelu http://sequel.rubyforge.org/rdoc/classes/Sequel/Model.html i dopiero tutaj zaczyna sie ORM.

W skrocie Sequel::Model mapuje obiekty na Sequel ktory jest mapowany na SQL :slight_smile:

sequel jest całkiem spoko, aczkolwiek ogólnie wolę datamappera/activerecord (chociaż w datamapperze jeszcze niewiele robiłem) - sequel jest bliżej bazy danych, nawet jako ORM z Sequel::Model. Sequela używałem z kolei w prostych skryptach do automatyzacji niektórych rzeczy na serwerze - tutaj sprawdził się bardzo fajnie.

Jeżeli chodzi o datamappera, to mam nadzieję, że jego rozwój nabierze rozpędu w wyniku wsparcia społeczności merba, bo cały czas jeszcze mu trochę rzeczy brakuje. Ostatnio na przykład kumpel spędził 2 godziny na debugowaniu jakiegoś dziwnego błędu w 0.9.7 - dopiero update do wersji rozwojowej pomógł.

UPDATE:
Dzisiaj wpadłem na ciekawą prezentację Katza: http://mwrc2008.confreaks.com/04katz.html
Warto obejrzeć.

Pisząc Sequel, również Sequel::Model miałem na myśli. Swego czasu w AR brakowało mi możliwości połączenia :select z :include oraz w miarę inteligentnego preloadingu, więc zacząłem eksperymenty z Sequelem. Finalnie Sequel jednak poległ w moim projekcie, bo tak modyfikował Array (open classes), że nie dało się używać go obok AR. Był to spory projekt, więc nie chciałem ingerować w nieswoje modele. Pozostała zabawa z find_by_sql, select_all etc. Ach, gdyby wtedy istniał ActiveHibernate.

Jesli chodzi o DM to jest wiele problemow, biblioteka jest lepiej zaprojektowana od AR, ale nadal w porownaniu do AR ma zbyt duzo bledow jelsi chodzi o relacje many2many albo takie rzeczy jak usuwanie obiektow relacji czy opcje :dependent. Napewno robi wrazenie ilosc dodatkow do DM oraz takie rzeczy jak repository i mozlwisoc pracy nie tylko z bazami danych, zastanwiam sie kiedy ktos zrobi dm-db4o :slight_smile:

Chcialem portowac moj projekt na dm ale po ilosci bledow jakie w nim sa mam powoli dosyc DM, zwlaszcza ze rozwoj w najblizszych miesiacach imho zwolni z uwagi na merge merb’a z rails. DM dostal ostrego kopa wasnie dzieki merbowi, aktualnie nikt o zdrowych zmyslach nie bedzie zaczynal projektu od merba po to aby za rok wrocic spowrotem do rails, a wiec wszyscy ci ktorzy sa przy rails, zostana, ci ktorzy przy merbie zostana… ale nie bedzie juz wiecej migracji. Tak wiec dopuki rails nie bedzie ORM agnostic doputy merb bedzie zwalnial w rozwoju, aby potem znow dostac ostrego kopa :stuck_out_tongue:

Dobra… rozpisalem sie znow na temat moich teori spiskowych :wink: a tak naprawde chcailem tylko poslodzic troche Sequelowi. Probowalem tej biblioteki jeszcze za czasow wydania 1.0, prawie rok temu. Biblioteka od tamtego czasu sie diametranie zmienila, stala sie czystrza i przejrzystsza, nabrala dojrzalosci jesli chodzi o warstwe ruby -> baza danych i co chyba najwazniesze, wreszcie Sequel::Model jest uzywalny. Naprawde wspaniale wyglada to w akcji. Osobiscie wlasnei sequel uwazam za przyszlosci ORM w ruby. ORM w przypadku sequela jest budowany znacznie bardziej przejrzyscie. podstawowy sequel nie zawiera np polimorfizmu, ale dzieki niesamowitej elastycznosci bardzo latwo mozna bylo dodac ta funkcjonalnosc.

Polecam porowannie przykladu :wink: Jak wyglada Model w sequelu przed a jak po :wink:

takie rzeczy jak konfigurowalny eager loader (mozna implementowac wlasna, mega-popieprzona logike ORM’a i ja optymalizowac!!!) czy naprawde fajny sposob na pluginy w sequelu stanowi niesamowity atut tej bibliteki dla developera :wink:

Wsytarczy spojrzec jak latwo i wedlug okreslonego schematu mozna wpiaz not naughty validation framework do sequela

Sequel Timestamped jest jeszcze prostrze

http://github.com/bricooke/sequel_timestamped/tree/master/lib/sequel_timestamped.rb

Zamirzam przez najblizsze kilka dni ostro potestowac nowe mozliwosci sequela, naprawde wygalda to znacznie lepiej niz rok temu, osobiscie dziwi mnie tak slaba popularnosc sequela. Jesli merb jest lepiej zaprojektowanym rails, to napewno sequel:model jest lepiej zaprojektowanym AR :wink: z naprawde konkretnym API!

Na koniec atuty sequela:

  • Sequel provides thread safety, connection pooling and a concise DSL for constructing database queries and table schemas.
  • Sequel also includes a lightweight but comprehensive ORM layer for mapping records to Ruby objects and handling associated records.
  • Sequel supports advanced database features such as prepared statements, bound variables, master/slave configurations, and database sharding.
  • Sequel makes it easy to deal with multiple records without having to break your teeth on SQL.
  • Sequel currently has adapters for ADO, DB2, DBI, Informix, JDBC, MySQL, ODBC, OpenBase, Oracle, PostgreSQL and SQLite3.

Database Sharding wymiata ;] http://sequel.rubyforge.org/rdoc/files/doc/sharding_rdoc.html

Filtering http://sequel.rubyforge.org/rdoc/files/doc/dataset_filtering_rdoc.html

Prepared Statment http://sequel.rubyforge.org/rdoc/files/doc/prepared_statements_rdoc.html

No i wreszcie advanced associations http://sequel.rubyforge.org/rdoc/files/doc/advanced_associations_rdoc.html

Polecam tez artykol http://www.infoq.com/news/2008/11/sequel-ruby-db-toolkit

Nieźle :slight_smile:

Będę musiał się przyjrzeć. Sequela używałem dawno temu, widzę że znowu trzeba będzie sprawdzić projekt.

Jest jeszcze wbudowane cache. Ktore mozna zmienic bardzo latwo :wink:

Wlasnie przygotowuje sobie prosty system cachowania oparty o WeakHash

[code]require ‘weakref’

class WeakHash
attr_accessor :cache
def initialize
prepare_cache
end

def prepare_cache
@cache = {}
@cache = WeakRef.new(@cache)
end

def
begin
@cache[key]
rescue WeakRef::RefError
nil
end
end
def []=(key, value)
@cache[key] = value
end

def inspect
begin
super
rescue WeakRef::RefError
prepare_cache
super
end
end
end[/code]
Ogolnie dziala, brakuje tylko metod get i set (bez ttl)

Ogolnie to + set_cache bylo by odpowiednikiem IdentityMap http://datamapper.org/doku.php?id=docs:identity_map bez uzywania tego durnego bloku repository {}

Tylko wyglada na to ze moja i tak jest gorsza od tej http://209.85.129.132/search?q=cache%3Ahttp%3A%2F%2Feigenclass.org%2Fhiki%2Fdeferred-finalizers-in-Ruby&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a