Szukaj…


Wprowadzenie

Realm Mobile Database jest alternatywą dla SQLite. Realm Mobile Database jest znacznie szybsza niż ORM i często szybsza niż surowy SQLite.

Korzyści

Funkcjonalność offline, Szybkie zapytania, Bezpieczne wątki, Aplikacje wieloplatformowe, Szyfrowanie, Architektura reaktywna.

Uwagi

Korzystając z Realm, musisz pamiętać, że nie wolno przekazywać RealmObjects, RealmResults i instancji Realm między wątkami. Jeśli potrzebujesz zapytania do danego wątku, otwórz instancję Realm w tym wątku. Po zakończeniu wątku powinieneś zamknąć królestwo.

UWAGA PRAWNA : Użytkownik rozumie, że Oprogramowanie może zawierać funkcje kryptograficzne, które mogą podlegać ograniczeniom eksportowym, oraz oświadcza i gwarantuje, że nie znajduje się w kraju objętym ograniczeniami eksportowymi lub embargiem Stanów Zjednoczonych, w tym Kubą, Iranem, Północą Korei, Sudanu, Syrii lub regionu Krymu oraz że nie znajdujesz się na liście Departamentu Handlu osób odmówionych, niezweryfikowanych stron lub powiązanych z podmiotem objętym ograniczeniami.

Dodanie królestwa do twojego projektu

Dodaj następującą zależność do pliku build.gradle poziomie projektu .

dependencies {
    classpath "io.realm:realm-gradle-plugin:3.1.2"
}

Dodaj następujące polecenie u góry pliku build.gradle poziomie aplikacji .

apply plugin: 'realm-android'

Wykonaj synchronizację stopniową, a teraz dodałeś Realm jako zależność do swojego projektu!

Realm wymaga początkowego wywołania od wersji 2.0.0 przed jego użyciem. Możesz to zrobić w klasie Application lub w metodzie onCreate pierwszej aktywności.

Realm.init(this); // added in Realm 2.0.0
Realm.setDefaultConfiguration(new RealmConfiguration.Builder().build());

Modele królestwa

Modele dziedzin muszą rozszerzać RealmObject bazową RealmObject , definiują schemat bazowej bazy danych.

Obsługiwane typy pól są boolean , byte , short , int , long , float , double , String , Date , byte[] , linki do innych RealmObject s, a RealmList<T extends RealmModel> .

public class Person extends RealmObject {
    @PrimaryKey //primary key is also implicitly an @Index 
                //it is required for `copyToRealmOrUpdate()` to update the object.
    private long id;

    @Index //index makes queries faster on this field
    @Required //prevents `null` value from being inserted
    private String name; 

    private RealmList<Dog> dogs; //->many relationship to Dog

    private Person spouse; //->one relationship to Person

    @Ignore
    private Calendar birthday; //calendars are not supported but can be ignored

    // getters, setters
}

Jeśli dodasz (lub usuniesz) nowe pole do swojego RealmObject (lub dodasz nową klasę RealmObject lub usuniesz istniejącą), konieczna będzie migracja . Można też ustawić deleteIfMigrationNeeded() w RealmConfiguration.Builder lub określić konieczną migrację. Migracja jest również wymagana przy dodawaniu (lub usuwaniu) @Required , @Index lub @PrimaryKey .

Relacje muszą być ustawiane ręcznie, NIE są one automatyczne na podstawie kluczy podstawowych.

Od wersji 0.88.0 w klasach RealmObject można również używać pól publicznych zamiast pól prywatnych / pobierających / ustawiających.

Możliwe jest także zaimplementowanie RealmModel zamiast rozszerzenia RealmObject , jeśli klasa jest również opatrzona adnotacjami @RealmClass .

@RealmClass
public class Person implements RealmModel {
    // ...
}

W takim przypadku metody takie jak person.deleteFromRealm() lub person.addChangeListener() są zastępowane przez RealmObject.deleteFromRealm(person) i RealmObject.addChangeListener(person) .

Ograniczenia są takie, że przez RealmObject tylko RealmObject może być rozszerzony i nie ma obsługi pól final , volatile i transient .

Ważne jest, aby zarządzana klasa RealmObject mogła być modyfikowana tylko w transakcji. Zarządzanego obiektu RealmObject nie można przekazywać między wątkami.

Lista prymitywów (RealmList )

Dziedzina obecnie nie obsługuje przechowywania listy prymitywów. Jest na ich liście rzeczy do zrobienia ( GitHub nr 575 ), ale tymczasem jest to obejście.

Utwórz nową klasę dla swojego prymitywnego typu, używa ona Integer, ale zmień ją na cokolwiek, co chcesz przechowywać.

public class RealmInteger extends RealmObject {
    private int val;

    public RealmInteger() {
    }

    public RealmInteger(int val) {
        this.val = val;
    }

    // Getters and setters
}

Możesz teraz użyć tego w swoim RealmObject .

public class MainObject extends RealmObject {

    private String name;
    private RealmList<RealmInteger> ints;

    // Getters and setters
}

Jeśli używasz GSON do wypełnienia swojego RealmObject , musisz dodać adapter typu niestandardowego.

Type token = new TypeToken<RealmList<RealmInteger>>(){}.getType();
Gson gson = new GsonBuilder()
        .setExclusionStrategies(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return f.getDeclaringClass().equals(RealmObject.class);
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return false;
            }
        })
        .registerTypeAdapter(token, new TypeAdapter<RealmList<RealmInteger>>() {

            @Override
            public void write(JsonWriter out, RealmList<RealmInteger> value) throws IOException {
                // Empty
            }

            @Override
            public RealmList<RealmInteger> read(JsonReader in) throws IOException {
                RealmList<RealmInteger> list = new RealmList<RealmInteger>();
                in.beginArray();
                while (in.hasNext()) {
                    list.add(new RealmInteger(in.nextInt()));
                }
                in.endArray();
                return list;
            }
        })
        .create();

try-with-resources

try (Realm realm = Realm.getDefaultInstance()) {
    realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                //whatever Transaction that has to be done
            }
    });
    //No need to close realm in try-with-resources
}

Try z zasobami może być używany tylko z KITKAT (minSDK 19)

Posortowane zapytania

Aby posortować zapytanie, zamiast używać findAll() , należy użyć findAllSorted() .

RealmResults<SomeObject> results = realm.where(SomeObject.class)
                                            .findAllSorted("sortField", Sort.ASCENDING);

Uwaga:

sort() zwraca całkowicie nową posortowaną RealmResults, ale aktualizacja tego RealmResults ją zresetuje. Jeśli używasz sort() , zawsze powinieneś sortować go ponownie w RealmChangeListener , usuń RealmChangeListener z poprzednich RealmResults i dodaj go do zwracanych nowych RealmResults . Użycie sort() w RealmResults zwróconej przez zapytanie asynchroniczne, które nie jest jeszcze załadowane, zakończy się niepowodzeniem.

findAllSorted() zawsze zwraca wyniki posortowane według pola, nawet jeśli zostanie zaktualizowane. Zaleca się użycie findAllSorted() .

Zapytania asynchroniczne

Każda metoda synchronicznego zapytania (taka jak findAll() lub findAllSorted() ) ma asynchroniczny odpowiednik ( findAllAsync() / findAllSortedAsync() ).

Zapytania asynchroniczne odciążają ocenę RealmResults do innego wątku. Aby otrzymać te wyniki w bieżącym wątku, bieżący wątek musi być wątkiem looper (czytaj: zapytania asynchroniczne zwykle działają tylko w wątku interfejsu użytkownika).

RealmChangeListener<RealmResults<SomeObject>> realmChangeListener; // field variable

realmChangeListener = new RealmChangeListener<RealmResults<SomeObject>>() {
    @Override
    public void onChange(RealmResults<SomeObject> element) {
        // asyncResults are now loaded
        adapter.updateData(element);
    }
};

RealmResults<SomeObject> asyncResults = realm.where(SomeObject.class).findAllAsync();
asyncResults.addChangeListener(realmChangeListener);

Używanie Realm z RxJava

W przypadku zapytań Realm udostępnia realmResults.asObservable() . Obserwowanie wyników jest możliwe tylko w wątkach chwytacza (zazwyczaj wątku interfejsu użytkownika).

Aby to zadziałało, konfiguracja musi zawierać następujące elementy

realmConfiguration = new RealmConfiguration.Builder(context)       //
                          .rxFactory(new RealmObservableFactory()) //
                             //...
                          .build();

Następnie możesz wykorzystać swoje wyniki jako obserwowalne.

Observable<RealmResults<SomeObject>> observable = results.asObservable();

W przypadku zapytań asynchronicznych należy filtrować wyniki według funkcji isLoaded() , aby zdarzenie było odbierane tylko wtedy, gdy zapytanie zostało wykonane. Ten filter() nie jest potrzebny w przypadku zapytań synchronicznych ( isLoaded() zawsze zwraca wartość true w przypadku zapytań synchronicznych).

    Subscription subscription = RxTextView.textChanges(editText).switchMap(charSequence -> 
        realm.where(SomeObject.class)
             .contains("searchField", charSequence.toString(), Case.INSENSITIVE)
             .findAllAsync()
             .asObservable())
    .filter(RealmResults::isLoaded) //
    .subscribe(objects -> adapter.updateData(objects));

W przypadku zapisu należy użyć metody executeTransactionAsync() lub otworzyć instancję Realm w wątku w tle, wykonać transakcję synchronicznie, a następnie zamknąć instancję Realm.

public Subscription loadObjectsFromNetwork() {
    return objectApi.getObjects()
        .subscribeOn(Schedulers.io())
        .subscribe(response -> {
            try(Realm realmInstance = Realm.getDefaultInstance()) {
                realmInstance.executeTransaction(realm -> realm.insertOrUpdate(response.objects));
            }
        });
}

Podstawowe użycie

Konfigurowanie instancji

Aby użyć Realm, musisz najpierw uzyskać jego instancję. Każde wystąpienie Realm jest mapowane na plik na dysku. Najbardziej podstawowym sposobem uzyskania instancji jest:

// Create configuration
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(context).build();

// Obtain realm instance
Realm realm = Realm.getInstance(realmConfiguration);
// or
Realm.setDefaultConfiguration(realmConfiguration);
Realm realm = Realm.getDefaultInstance();

Metoda Realm.getInstance() tworzy plik bazy danych, jeśli nie został utworzony, w przeciwnym razie otwiera plik. Obiekt RealmConfiguration kontroluje wszystkie aspekty tworzenia dziedziny - czy jest to inMemory() danych inMemory() , nazwa pliku Realm, czy dziedzina powinna zostać wyczyszczona, jeśli potrzebna jest migracja, dane początkowe itp.

Należy pamiętać, że wywołania Realm.getInstance() są liczone jako referencje (każde wywołanie zwiększa licznik), a licznik jest zmniejszany, gdy realm.close() jest realm.close() .

Zamykanie instancji

W wątkach w tle bardzo ważne jest zamknięcie instancji Realm, gdy nie są one już używane (na przykład transakcja jest zakończona i kończy się wykonywanie wątku). Brak zamknięcia wszystkich instancji Realm w wątku w tle powoduje przypięcie wersji i może spowodować duży wzrost rozmiaru pliku.

Runnable runnable = new Runnable() {
    Realm realm = null;
    try {
        realm = Realm.getDefaultInstance();
        // ...
    } finally {
        if(realm != null) {
            realm.close();
        }
    }
};

new Thread(runnable).start(); // background thread, like `doInBackground()` of AsyncTask

Warto zauważyć, że powyżej poziomu API 19 możesz zastąpić ten kod tylko:

try(Realm realm = Realm.getDefaultInstance()) {
    // ...
}

Modele

Następnym krokiem byłoby stworzenie twoich modeli. Tutaj można zadać pytanie „co to jest model?”. Model jest strukturą, która określa właściwości obiektu przechowywanego w bazie danych. Na przykład poniżej modelujemy książkę.

public class Book extends RealmObject {
 
    // Primary key of this entity
    @PrimaryKey
    private long id;
 
    private String title;

    @Index // faster queries
    private String author;
 
    // Standard getters & setter
    public long getId() {
        return id;
    }
 
    public void setId(long id) {
        this.id = id;
    }
 
    public String getTitle() {
        return title;
    }
 
    public void setTitle(String title) {
        this.title = title;
    }
 
    public String getAuthor() {
        return author;
    }
 
    public void setAuthor(String author) {
        this.author = author;
    }
}

Pamiętaj, że twoje modele powinny rozszerzyć klasę RealmObject. Klucz podstawowy jest także określony w adnotacji @PrimaryKey . Klucze podstawowe mogą mieć wartość null, ale tylko jeden element może mieć null jako klucz podstawowy. Możesz także użyć adnotacji @Ignore dla pól, których nie należy @Ignore na dysku:

@Ignore
private String isbn;

Wstawianie lub aktualizowanie danych

Aby zapisać obiekt książki w instancji bazy danych Realm, możesz najpierw utworzyć instancję swojego modelu, a następnie zapisać ją w bazie danych za copyToRealm metody copyToRealm . Do tworzenia lub aktualizacji możesz użyć copyToRealmOrUpdate . ( insertOrUpdate() alternatywą jest nowo dodana insertOrUpdate() ).

// Creating an instance of the model
Book book = new Book();
book.setId(1);
book.setTitle("Walking on air");
book.setAuthor("Taylor Swift")

// Store to the database
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        realm.insertOrUpdate(book);
    }
});

Pamiętaj, że wszystkie zmiany danych muszą nastąpić w transakcji. Innym sposobem utworzenia obiektu jest użycie następującego wzorca:

Book book = realm.createObject(Book.class, primaryKey);
...

Zapytanie do bazy danych

  • Wszystkie książki:

    RealmResults<Book> results = realm.where(Book.class).findAll();
    
  • Wszystkie książki o identyfikatorze większym niż 10:

    RealmResults<Book> results = realm.where(Book.class)
                                      .greaterThan("id", 10)
                                      .findAll();
    
  • Książki autorstwa 'Taylor Swift' lub '%Peter%' :

    RealmResults<Book> results = realm.where(Book.class)
                                      .beginGroup()
                                          .equalTo("author", "Taylor Swift")
                                          .or()
                                          .contains("author", "Peter")
                                      .endGroup().findAll();
    

Usuwanie obiektu

Na przykład chcemy usunąć wszystkie książki autorstwa Taylor Swift:

// Start of transaction
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        // First Step: Query all Taylor Swift books
        RealmResults<Book> results = ...
        
        // Second Step: Delete elements in Realm
        results.deleteAllFromRealm();
    }
});


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow