Realm to najlepsze rozwiązanie bazodanowe dla systemu Android
Opublikowany: 2022-03-11Odkąd powstał system Android, my, twórcy aplikacji, używamy SQLite do przechowywania naszych danych lokalnych. Czasami bezpośrednio z instrukcjami SQL, czasami używając Object-Relational Mapper (ORM) jako warstwy abstrakcji, ale tak czy inaczej, używaliśmy SQLite pod koniec dnia.
Pomimo wszystkich zalet SQLite, zdarzały się jednak chwile, kiedy żałowaliśmy, że nie mamy alternatywy dla modelu relacyjnego: coś, co mogłoby oszczędzić nam konieczności dodawania standardowego kodu w celu konwersji wartości do iz bazy danych lub pozwolić nam pominąć konfigurowanie mapowań między klasami i tabelami, polami i kolumnami, kluczami obcymi itp.
Innymi słowy, baza danych ze strukturami danych bardziej podobnymi do tych, których faktycznie używamy na poziomie aplikacji. Jeszcze lepiej, gdyby z założenia była wydajna pod względem pamięci, pozwalając na lepsze wrażenia w urządzeniach o ograniczonych zasobach, byłoby to niesamowite.
Są to w rzeczywistości niektóre z gotowych korzyści, jakie otrzymujemy dzięki Realm, platformie bazodanowej o odrębnej architekturze, która pojawiła się jako nowa alternatywa dla SQLite.
W tym artykule przedstawiono niektóre z głównych powodów, dla których Realm przyciągnęło tak wiele uwagi i dlaczego warto rozważyć jego wypróbowanie. Omówiono w nim niektóre z kluczowych zalet, jakie Realm zapewnia programistom Androida w porównaniu z SQLite.
Ponieważ Realm jest dostępny na wielu platformach, niektóre z tego, co zostanie omówione w tym artykule, mają również znaczenie dla innych platform mobilnych, takich jak iOS, Xamarin i React Native.
SQLite: Działa, ale najczęściej nie jest to potrzebne
Większość programistów mobilnych prawdopodobnie zna SQLite. Istnieje od 2000 roku i jest prawdopodobnie najczęściej używanym silnikiem relacyjnej bazy danych na świecie.
SQLite ma wiele zalet, które wszyscy uznajemy, a jedną z nich jest natywna obsługa systemu Android.
Fakt, że jest to standardowa relacyjna baza danych SQL, również minimalizuje krzywą uczenia się dla tych, którzy wywodzą się z relacyjnych baz danych. Zapewnia również dość dobrą wydajność, jeśli zostanie wykorzystany w pełni (wykorzystując funkcje, takie jak przygotowane wyciągi, operacje zbiorcze na transakcjach itp.). Chociaż SQLite może nie skalować się zbyt dobrze dla wszystkich Twoich potrzeb.
Bezpośrednie radzenie sobie z instrukcjami SQL ma jednak wiele wad.
Zgodnie z oficjalną dokumentacją Androida, oto kroki potrzebne do rozpoczęcia czytania/pisania w SQLite:
- Opisz swój schemat w kategoriach klas kontraktów.
- Zdefiniuj swoje polecenia tworzenia/upuszczania tabeli w ciągach.
- Rozszerz
SQLiteOpenHelper
o uruchamianie poleceń tworzenia i zarządzanie aktualizacjami/obniżaniem wersji.
Gdy to zrobisz, będziesz gotowy do czytania i zapisywania w swojej bazie danych. Jednak konieczne będzie przekonwertowanie między obiektami w aplikacji i wartościami w bazie danych. Krótko mówiąc: to dużo standardowego kodu!
Kolejną kwestią jest łatwość utrzymania. W miarę jak Twój projekt się rozrasta i pojawia się potrzeba pisania bardziej złożonych zapytań, otrzymasz duże porcje nieprzetworzonych zapytań SQL w ciągach. Jeśli później będziesz musiał zmienić logikę tych zapytań, może to być dość kłopotliwe.
Pomimo swoich wad, zdarzają się przypadki, w których używanie surowego SQL jest najlepszą opcją. Jednym z przykładów jest tworzenie biblioteki, w której wydajność i rozmiar są czynnikami krytycznymi, a jeśli to możliwe, należy unikać dodawania biblioteki innej firmy.
Mapper obiektowo-relacyjny: pomoc w wyzwaniach SQL
Aby uchronić nas przed zajmowaniem się surowym SQL, na ratunek przyszły ORM.
Niektóre z najbardziej znanych ORM dla Androida to DBFlow, greenDAO i OrmLite.
Największą wartością, jaką przynoszą, jest abstrakcja SQLite, która pozwala nam stosunkowo łatwo mapować encje bazy danych na obiekty Java.
Wśród innych korzyści programiści aplikacji mogą pracować z obiektami, znacznie bardziej znaną strukturą danych. Pomaga również w utrzymaniu, ponieważ teraz zajmujemy się obiektami wysokiego poziomu z silniejszym typowaniem i pozostawiając brudną robotę bibliotekom. Mniej zmagania się z budowaniem zapytań poprzez łączenie ciągów lub ręczną obsługę połączenia z bazą danych. Mniej literówek.
Chociaż faktem jest, że te ORM podniosły poprzeczkę w zakresie baz danych Androida, mają też swoje wady. W wielu przypadkach ładujesz niepotrzebne dane.
Oto przykład.
Załóżmy, że masz tabelę z 15 kolumnami, a na określonym ekranie aplikacji wyświetlana jest lista obiektów z tej tabeli. Ta lista wyświetla wartości tylko z trzech kolumn. Dlatego ładując wszystkie dane z wiersza tabeli, otrzymujesz pięć razy więcej danych, niż faktycznie potrzebujesz na tym ekranie.
Prawdę mówiąc, w niektórych z tych bibliotek możesz z góry określić, które kolumny chcesz pobrać, ale w tym celu musisz dodać kolejny kod, a mimo to nie wystarczy, jeśli będziesz wiedzieć dokładnie, które kolumny chcesz użyj po przejrzeniu samych danych: niektóre dane mogą i tak być niepotrzebnie ładowane.
Ponadto często zdarzają się scenariusze, w których trzeba wykonać złożone zapytania, a biblioteka ORM po prostu nie oferuje sposobu opisywania tych zapytań za pomocą interfejsu API. To może na przykład sprawić, że będziesz pisać nieefektywne zapytania, które wykonują więcej obliczeń niż to, czego potrzebujesz.
Konsekwencją jest spadek wydajności, co prowadzi do uciekania się do surowego SQL. Chociaż dla wielu z nas nie jest to zerwanie umowy, szkodzi to głównemu celowi mapowania obiektowo-relacyjnego i prowadzi nas z powrotem do niektórych z wyżej wymienionych problemów dotyczących SQLite.
Królestwo: doskonała alternatywa
Realm Mobile Database to baza danych zaprojektowana od podstaw dla urządzeń mobilnych.
Kluczowa różnica między Realm a ORM-ami polega na tym, że Realm nie jest abstrakcją zbudowaną na bazie SQLite, ale zupełnie nowym silnikiem bazy danych. Zamiast modelu relacyjnego, opiera się na składnicy obiektów. Jego rdzeń składa się z samodzielnej biblioteki C++. Obecnie obsługuje systemy Android, iOS (Objective-C i Swift), Xamarin i React Native.
Realm wystartował w czerwcu 2014 roku, a więc obecnie ma już dwa i pół roku (całkiem nowy!).
Podczas gdy technologie baz danych serwerów przechodziły rewolucję od 2007 roku i pojawiło się wiele nowych, technologia baz danych dla urządzeń mobilnych utknęła w SQLite i jego opakowaniach. To była jedna z kluczowych motywacji do stworzenia czegoś od podstaw. Co więcej, jak zobaczymy, niektóre funkcje Realm wymagały fundamentalnych zmian w zachowaniu bazy danych na niskim poziomie, a zbudowanie czegoś na bazie SQLite nie było możliwe.
Ale czy Realm naprawdę jest tego warte? Oto najważniejsze powody, dla których warto rozważyć dodanie Realm do swojego paska narzędzi.
Łatwe modelowanie
Oto przykład niektórych modeli stworzonych za pomocą Realm:
public class Contact extends RealmObject { @PrimaryKey String id; protected String name; String email; @Ignore public int sessionId; //Relationships private Address address; private RealmList<Contact> friends; //getters & setter left out for brevity }
public class Address extends RealmObject { @PrimaryKey public Long id; public String name; public String address; public String city; public String state; public long phone; }
Twoje modele pochodzą z RealmObject. Realm akceptuje wszystkie typy podstawowe i ich typy pudełkowe (z wyjątkiem char
), String
, Date
i byte[]
. Obsługuje również podklasy RealmObject
i RealmList<? extends RealmObject>
RealmList<? extends RealmObject>
na relacje modelu.
Pola mogą mieć dowolny poziom dostępu (prywatny, publiczny, chroniony itp.). Wszystkie pola są domyślnie utrwalane i wystarczy dodać adnotacje do pól „specjalnych” (np. @PrimaryKey
dla pola klucza podstawowego, @Ignore
, aby ustawić nieutrwalone pola itp.).
Interesujące w tym podejściu jest to, że utrzymuje ono klasy mniej „zanieczyszczone adnotacjami” w porównaniu z ORM-ami, ponieważ w większości z nich potrzebne są adnotacje do mapowania klas na tabele, zwykłych pól do kolumn bazy danych, pól kluczy obcych do innych tabel itd. na.
Relacje
Jeśli chodzi o relacje, istnieją dwie opcje:
Dodaj model jako pole z innego modelu. W naszym przykładzie klasa
Contact
zawiera poleAddress
, które definiuje ich relację. Kontakt może mieć adres, ale nic nie stoi na przeszkodzie, aby ten sam adres został dodany do innych kontaktów. To pozwala na relacje jeden-do-jednego i jeden-do-wielu.Dodaj
RealmList
modeli, do których się odwołujesz.RealmLists
zachowują się jak stare dobreLists
Java , działając jako kontener obiektów Realm. Widzimy, że nasz modelContact
maRealmList
kontaktów, które w tym przykładzie są jej przyjaciółmi. Dzięki temu podejściu można modelować relacje jeden-do-wielu i wiele-do-wielu.
Podoba mi się ten sposób przedstawiania relacji, ponieważ jest to dla nas, programistów Java, bardzo naturalne. Dodając te obiekty (lub listy tych obiektów) bezpośrednio jako pola naszej klasy, tak jak zrobilibyśmy to dla innych klas niemodelowych, nie musimy zajmować się ustawieniami SQLite dla kluczy obcych.
Zastrzeżenie: Nie ma obsługi dziedziczenia modelu. Bieżącym obejściem jest użycie kompozycji. Jeśli więc, na przykład, masz model Animal
i masz nadzieję, że utworzysz model Dog
rozciągający się od Animal
, zamiast tego będziesz musiał dodać instancję Animal
jako pole w Dog
. Trwa wielka debata na temat kompozycji a dziedziczenia. Jeśli lubisz korzystać z dziedziczenia, jest to zdecydowanie coś, co musisz wiedzieć o Realm. W SQLite można to zaimplementować za pomocą dwóch tabel (jednej dla rodzica i jednej dla dziecka) połączonych kluczem obcym. Niektóre ORM-y również nie nakładają tego ograniczenia, np. DBFlow.
Odzyskaj tylko potrzebne dane! Projekt bez kopii
To zabójcza funkcja.
Realm stosuje koncepcję projektu z zerową kopią, co oznacza, że dane nigdy nie są kopiowane do pamięci. Wyniki otrzymane z zapytania są w rzeczywistości tylko wskaźnikami do rzeczywistych danych. Same dane są leniwie ładowane, gdy masz do nich dostęp.
Na przykład masz model z 10 polami (kolumnami w SQL). Jeśli zapytasz o obiekty tego modelu, aby wyświetlić je na ekranie, a potrzebujesz tylko trzech z 10 pól do wypełnienia elementów listy, będą to jedyne pobrane pola.
W rezultacie zapytania są niesamowicie szybkie (patrz tutaj i tutaj, aby uzyskać wyniki testów porównawczych).
Jest to duża przewaga nad ORM-ami, które zazwyczaj z góry ładują wszystkie dane z wybranych wierszy SQL.
W rezultacie ładowanie ekranu staje się znacznie wydajniejsze bez dodatkowego wysiłku ze strony programisty: to tylko domyślne zachowanie Realm.
Ponadto oznacza to również, że aplikacje zużywają mniej pamięci, a biorąc pod uwagę, że mówimy o środowisku o ograniczonych zasobach, takim jak urządzenia mobilne, może to mieć duże znaczenie.
Inną konsekwencją podejścia z zerową kopią jest to, że obiekty zarządzane przez Realm są automatycznie aktualizowane.
Dane nigdy nie są kopiowane do pamięci. Jeśli masz wyniki z zapytania, a inny wątek zaktualizował te dane w bazie danych po zapytaniu, wyniki, które posiadasz, będą już odzwierciedlać te zmiany. Twoje wyniki są tylko wskaźnikami do rzeczywistych danych. Tak więc, gdy uzyskujesz dostęp do wartości z pól, zwracane są najbardziej aktualne dane.
Jeśli odczytałeś już dane z obiektów Realm i wyświetlałeś je na przykład na ekranie, i chcesz otrzymywać aktualizacje, gdy zmienią się dane bazowe, możesz dodać detektor:
final RealmResults<Contact> johns = realm.where(Contact.class).beginsWith("name", "John ").findAll(); johns.addChangeListener(new RealmChangeListener<RealmResults<Contact>>() { @Override public void onChange(RealmResults<Contact> results) { // UPDATE UI } });
To nie tylko opakowanie
Chociaż mamy dziesiątki opcji dla ORM-ów, są one wrapperami, a wszystko sprowadza się do SQLite pod spodem, co ogranicza ich zasięg. W przeciwieństwie do tego, Realm nie jest tylko kolejnym opakowaniem SQLite. Ma swobodę zapewniania funkcji, których ORM po prostu nie mogą zaoferować.
Jedną z fundamentalnych zmian w Realm jest możliwość przechowywania danych jako magazynu wykresów obiektów.
Oznacza to, że Realm to obiekty od samego początku, od poziomu języka programowania po bazę danych. W rezultacie podczas zapisywania i odczytywania wartości następuje znacznie mniejsza konwersja w porównaniu z relacyjną bazą danych.
Struktury baz danych dokładniej odzwierciedlają struktury danych używane przez programistów aplikacji. W rzeczywistości jest to jeden z głównych powodów, dla których następuje odejście od modelowania relacyjnego w kierunku modeli agregujących w rozwoju po stronie serwera. Realm wreszcie wprowadza niektóre z tych pomysłów do świata programowania mobilnego.
Jeśli pomyślimy o komponentach architektury Realm, na dole znajduje się jej rdzeń z najbardziej podstawową implementacją platformy. Oprócz tego będziemy mieć biblioteki wiążące do każdej obsługiwanej platformy.
Kiedy używasz wrappera dla jakiejś technologii, nad którą nie masz kontroli, ostatecznie musisz zapewnić wokół niej jakąś warstwę abstrakcji.
Biblioteki powiązań dziedzin są zaprojektowane tak, aby były jak najcieńsze, aby ograniczyć złożoność abstrakcji. W większości propagują ideę projektową z Core. Mając kontrolę nad całą architekturą, te komponenty działają lepiej ze sobą.
Praktycznym przykładem jest dostęp do innych obiektów, do których się odwołujemy (klucze obce w SQL). Struktura plików Realm jest oparta na łączach natywnych, więc gdy wyszukujesz relacje, zamiast tłumaczyć abstrakcję ORM na relacyjne i/lub łączyć wiele tabel, otrzymujesz surowe łącza do obiektów na poziomie systemu plików w formacie pliku.
To obiekty wskazujące bezpośrednio na inne obiekty. W związku z tym zapytanie o relację jest takie samo, jak na przykład zapytanie o kolumnę liczb całkowitych. Nie ma potrzeby wykonywania kosztownych operacji przechodzenia przez klucze obce. Chodzi o podążanie za wskazówkami.
Społeczność
Realm jest aktywnie rozwijany i dość często wydaje zaktualizowane wersje.
Wszystkie komponenty z Realm Mobile Database są typu open source. Są bardzo responsywni w swoim trackerze problemów i Stack Overflow, więc możesz spodziewać się dobrego i szybkiego wsparcia na tych kanałach.
Ponadto opinie społeczności są brane pod uwagę przy ustalaniu priorytetów problemów (błędy, ulepszenia, prośby o nowe funkcje itp.). Zawsze dobrze jest wiedzieć, że możesz mieć coś do powiedzenia w rozwoju używanych narzędzi.
Zacząłem używać Realm w 2015 roku i od tego czasu natknąłem się na kilka postów w sieci z różnymi opiniami na temat Realm. Niedługo porozmawiamy o jego ograniczeniach, ale zauważyłem, że wiele skarg złożonych w czasie postu zostało już naprawionych.
Kiedy dowiedziałem się na przykład o Realm, nie było jeszcze obsługi niestandardowych metod na modelach i wywołaniach asynchronicznych. W tamtym czasie dla wielu były to łamacze umów, ale oba są obecnie obsługiwane.
Taka szybkość rozwoju i responsywność daje nam większą pewność, że nie będziemy długo czekać na ważne funkcje.
Ograniczenia
Jak wszystko w życiu, Realm to nie tylko róże. Oprócz wspomnianego już przedawnienia dziedziczenia, należy pamiętać o innych niedociągnięciach:
Chociaż możliwe jest jednoczesne odczytywanie i zapisywanie wielu wątków do bazy danych, obiektów Realm nie można przenosić między wątkami . Jeśli na przykład pobierasz obiekt realm za pomocą funkcji
doInBackground()
, która działa w wątku w tle, nie możesz przekazać tej instancji do metodonPostExecute()
, ponieważ są one uruchamiane w wątku głównym. Możliwe obejście tej sytuacji to albo wykonanie kopii obiektu i przekazanie go dalej, albo przekazanie identyfikatora obiektu i ponowne pobranie obiektu za pomocąonPostExecute()
. Realm oferuje synchroniczne i asynchroniczne metody odczytu/zapisu.Klucze podstawowe z automatycznym przyrostem nie są obsługiwane , więc musisz sam zająć się ich generowaniem.
Nie jest możliwy dostęp do bazy danych z różnych procesów w tym samym czasie . Zgodnie z ich dokumentacją, wsparcie wieloprocesowe już wkrótce.
Realm to przyszłość mobilnych rozwiązań baz danych
SQLite to solidny, solidny i sprawdzony silnik bazy danych, a relacyjne bazy danych nie znikną w najbliższym czasie. Istnieje wiele dobrych ORM-ów, które przydadzą się również w wielu scenariuszach.
Jednak ważne jest, aby być na bieżąco z aktualnymi trendami.
W związku z tym uważam, że Realm to jeden z największych nadchodzących trendów ostatnich lat, jeśli chodzi o rozwój mobilnych baz danych.
Realm niesie ze sobą unikalne podejście do radzenia sobie z danymi, które są cenne dla programistów, nie tylko dlatego, że mogą być lepszą opcją niż istniejące rozwiązania, ale także dlatego, że poszerza nasze horyzonty w zakresie nowych możliwości i podnosi poprzeczkę w zakresie technologii mobilnych baz danych.
Czy masz już doświadczenie z Realm? Zachęcamy do dzielenia się swoimi przemyśleniami.