Zoeken…


Invoering

Realm Mobile Database is een alternatief voor SQLite. Realm Mobile Database is veel sneller dan een ORM en vaak sneller dan ruwe SQLite.

Voordelen

Offline functionaliteit, snelle zoekopdrachten, veilige threading, platformonafhankelijke apps, codering, reactieve architectuur.

Opmerkingen

Wanneer u Realm gebruikt, moet u onthouden dat u RealmObjects, RealmResults en Realm-instanties niet tussen threads mag doorgeven. Als u een query nodig hebt voor een bepaalde thread, opent u een Realm-instantie voor die thread. Bij het beëindigen van de thread, moet u het rijk afsluiten.

JURIDISCHE OPMERKING : U begrijpt dat de Software cryptografische functies kan bevatten die kunnen worden onderworpen aan exportbeperkingen, en u verklaart en garandeert dat u zich niet bevindt in een land dat onderworpen is aan exportbeperkingen of embargo van de Verenigde Staten, waaronder Cuba, Iran, Noord Korea, Soedan, Syrië of de Krim , en dat u niet op de lijst van het ministerie van Handel van geweigerde personen, niet-geverifieerde partijen of aangesloten bij een beperkte entiteit staat.

Realm toevoegen aan uw project

Voeg de volgende afhankelijkheid om uw project niveau build.gradle bestand.

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

Voeg het volgende toe rechtsboven in het build.gradle bestand op app- niveau.

apply plugin: 'realm-android'

Voltooi een gradle-synchronisatie en je hebt nu Realm toegevoegd als een afhankelijkheid van je project!

Realm vereist een eerste oproep sinds 2.0.0 voordat het wordt gebruikt. U kunt dit doen in uw Application of in de onCreate methode van uw eerste activiteit.

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

Realm-modellen

Realm-modellen moeten de RealmObject basisklasse uitbreiden, ze definiëren het schema van de onderliggende database.

Ondersteunde veldtypen zijn boolean , byte , short , int , long , float , double , String , Date , byte[] , links naar andere RealmObject s en 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
}

Als u een nieuw veld aan uw RealmObject toevoegt (of verwijdert) (of u voegt een nieuwe RealmObject-klasse toe of verwijdert een bestaande klasse), is een migratie nodig. U kunt deleteIfMigrationNeeded() in uw RealmConfiguration.Builder , of de benodigde migratie definiëren. Migratie is ook vereist bij het toevoegen (of verwijderen) van @Required , of @Index of @PrimaryKey annotatie.

Relaties moeten handmatig worden ingesteld, ze zijn NIET automatisch op basis van primaire sleutels.

Sinds 0.88.0 is het ook mogelijk om openbare velden te gebruiken in plaats van privévelden / getters / setters in RealmObject-klassen.

Het is ook mogelijk uit te voeren RealmModel plaats verlengen RealmObject , indien de klasse ook geannoteerd met @RealmClass .

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

In dat geval worden methoden zoals person.deleteFromRealm() of person.addChangeListener() vervangen door RealmObject.deleteFromRealm(person) en RealmObject.addChangeListener(person) .

Beperkingen zijn dat door een RealmObject alleen RealmObject kan worden uitgebreid en er geen ondersteuning is voor final , volatile en transient velden.

Het is belangrijk dat een beheerde RealmObject-klasse alleen in een transactie kan worden gewijzigd. Een beheerd RealmObject kan niet tussen threads worden doorgegeven.

Lijst met primitieven (RealmList )

Realm biedt momenteel geen ondersteuning voor het opslaan van een lijst met primitieven. Het staat op hun takenlijst ( GitHub-nummer 575 ), maar in de tussentijd is hier een oplossing.

Maak een nieuwe klasse voor uw primitieve type, deze gebruikt Integer, maar wijzig deze voor wat u maar wilt opslaan.

public class RealmInteger extends RealmObject {
    private int val;

    public RealmInteger() {
    }

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

    // Getters and setters
}

U kunt dit nu gebruiken in uw RealmObject .

public class MainObject extends RealmObject {

    private String name;
    private RealmList<RealmInteger> ints;

    // Getters and setters
}

Als u gebruik maakt GSON uw bevolken RealmObject , moet u een aangepaste soort adapter toe te voegen.

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-met-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
}

De Try with resources kan alleen worden gebruikt vanuit KITKAT (minSDK 19)

Gesorteerd zoekopdrachten

Om een zoekopdracht te sorteren, in plaats van findAll() te gebruiken, moet u findAllSorted() .

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

Notitie:

sort() retourneert een volledig nieuwe RealmResults die is gesorteerd, maar een update van deze RealmResults zal het resetten. Als u sort() , moet u dit altijd opnieuw sorteren in uw RealmChangeListener , de RealmChangeListener uit de vorige RealmResults en toevoegen aan de geretourneerde nieuwe RealmResults . Het gebruik van sort() op een RealmResults geretourneerd door een async-query die nog niet is geladen, zal mislukken.

findAllSorted() retourneert altijd de resultaten gesorteerd op veld, zelfs als deze wordt bijgewerkt. Het wordt aanbevolen om findAllSorted() .

Async-vragen

Elke synchrone findAll() zoals findAll() of findAllSorted() ) heeft een asynchrone tegenhanger ( findAllAsync() / findAllSortedAsync() ).

Asynchrone query's brengen de evaluatie van de RealmResults naar een andere thread. Om deze resultaten op de huidige thread te ontvangen, moet de huidige thread een looperdraad zijn (lees: async-query's werken meestal alleen op de UI-thread).

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);

Realm gebruiken met RxJava

Voor vragen biedt Realm de methode realmResults.asObservable() . Het observeren van resultaten is alleen mogelijk op looper-threads (meestal de UI-thread).

Om dit te laten werken, moet uw configuratie het volgende bevatten

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

Daarna kunt u uw resultaten gebruiken als waarneembaar.

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

Voor asynchrone query's moet u de resultaten filteren op isLoaded() , zodat u alleen een gebeurtenis ontvangt wanneer de query is uitgevoerd. Dit filter() is niet nodig voor synchrone query's ( isLoaded() retourneert altijd true bij synchronisatiequery's).

    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));

Voor schrijven moet u de methode executeTransactionAsync() gebruiken of een Realm-instantie op de achtergrondthread openen, de transactie synchroon uitvoeren en vervolgens de Realm-instantie sluiten.

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

Basisgebruik

Een exemplaar instellen

Om Realm te gebruiken, moet u er eerst een exemplaar van verkrijgen. Elke Realm-instantie wordt toegewezen aan een bestand op schijf. De meest eenvoudige manier om een exemplaar te krijgen is als volgt:

// 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();

De methode Realm.getInstance() maakt het databasebestand als het niet is gemaakt, anders wordt het bestand geopend. Het RealmConfiguration object regelt alle aspecten van hoe een Realm wordt gemaakt - of het nu een inMemory() -database is, de naam van het Realm-bestand, of de Realm moet worden gewist als een migratie nodig is, initiële gegevens, enz.

Houd er rekening mee dat aanroepen naar Realm.getInstance() worden geteld als referentie (elke aanroep verhoogt een teller) en de teller wordt verlaagd wanneer realm.close() wordt aangeroepen.

Een exemplaar sluiten

Op achtergrond threads, is het zeer belangrijk om het rijk instantie (s) sluiten zodra het niet meer wordt gebruikt (bijvoorbeeld transactie is voltooid en de uitvoering draadeinden). Als niet alle Realm-exemplaren op de achtergrondthread worden gesloten, wordt de versie vastgezet en kan de bestandsgrootte aanzienlijk toenemen.

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

Het is vermeldenswaard dat u boven API-niveau 19 deze code kunt vervangen door alleen dit:

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

modellen

De volgende stap is het maken van uw modellen. Hier kan een vraag worden gesteld: "wat is een model?". Een model is een structuur die de eigenschappen definieert van een object dat in de database wordt opgeslagen. Hieronder modelleren we bijvoorbeeld een boek.

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;
    }
}

Merk op dat uw modellen de RealmObject-klasse moeten uitbreiden. Primaire sleutel wordt ook gespecificeerd door @PrimaryKey annotatie. Primaire sleutels kunnen nul zijn, maar slechts één element kan null als primaire sleutel hebben. U kunt ook de annotatie @Ignore voor de velden die niet op de schijf mogen blijven staan:

@Ignore
private String isbn;

Gegevens invoegen of bijwerken

Om een boekobject in uw Realm-database-exemplaar op te slaan, kunt u eerst een exemplaar van uw model maken en dit vervolgens via de copyToRealm methode in de database copyToRealm . Voor het maken of bijwerken kunt u copyToRealmOrUpdate . (Een sneller alternatief is de nieuw toegevoegde 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);
    }
});

Houd er rekening mee dat alle wijzigingen in gegevens moeten plaatsvinden in een transactie. Een andere manier om een object te maken is met het volgende patroon:

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

De database opvragen

  • Alle boeken:

    RealmResults<Book> results = realm.where(Book.class).findAll();
    
  • Alle boeken met een ID groter dan 10:

    RealmResults<Book> results = realm.where(Book.class)
                                      .greaterThan("id", 10)
                                      .findAll();
    
  • Boeken door 'Taylor Swift' of '%Peter%' :

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

Een object verwijderen

We willen bijvoorbeeld alle boeken van Taylor Swift verwijderen:

// 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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow