Recherche…


Introduction

Realm Mobile Database est une alternative à SQLite. Realm Mobile Database est beaucoup plus rapide qu'un ORM, et souvent plus rapide que SQLite brut.

Avantages

Fonctionnalités hors ligne, requêtes rapides, threading sécurisé, applications multiplates-formes, cryptage, architecture réactive.

Remarques

Lorsque vous utilisez Realm, vous devez vous rappeler que vous ne devez pas transmettre les instances RealmObjects, RealmResults et Realm entre les threads. Si vous avez besoin d'une requête sur un thread donné, ouvrez une instance Realm sur ce thread. À la fin du thread, vous devez fermer le royaume.

NOTE JURIDIQUE : Vous comprenez que le Logiciel peut contenir des fonctions cryptographiques pouvant être soumises à des restrictions d’exportation, et vous déclarez et garantissez que vous ne vous trouvez pas dans un pays soumis à une restriction ou à un embargo des États-Unis, notamment Cuba, Iran La Corée, le Soudan, la Syrie ou la région de Crimée et que vous ne figurez pas sur la liste des personnes refusées, des parties non vérifiées du Département du commerce ou affiliées à une entité restreinte.

Ajouter un domaine à votre projet

Ajoutez la dépendance suivante à votre fichier build.gradle niveau du projet .

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

Ajoutez ce qui suit en haut de votre fichier build.gradle niveau de l' application .

apply plugin: 'realm-android'

Complétez une synchronisation progressive et vous avez maintenant Realm ajouté comme une dépendance à votre projet!

Realm nécessite un appel initial depuis 2.0.0 avant de l'utiliser. Vous pouvez le faire dans votre classe Application ou dans la méthode onCreate de votre première activité.

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

Modèles de domaine

Les modèles de domaine doivent étendre la classe de base RealmObject , ils définissent le schéma de la base de données sous-jacente.

Les types de champs pris en charge sont boolean , byte , caractères short , int , long , float , double , String , Date , byte[] , liens vers d'autres RealmObject RealmList<T extends RealmModel> et 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
}

Si vous ajoutez (ou supprimez) un nouveau champ à votre RealmObject (ou si vous ajoutez une nouvelle classe RealmObject ou en supprimez un existant), une migration sera nécessaire. Vous pouvez définir deleteIfMigrationNeeded() dans votre RealmConfiguration.Builder ou définir la migration nécessaire. La migration est également requise lors de l'ajout (ou de la suppression) des @Required , @Index ou @PrimaryKey .

Les relations doivent être définies manuellement, elles ne sont PAS automatiques en fonction des clés primaires.

Depuis la version 0.88.0, il est également possible d'utiliser des champs publics au lieu de champs / getters / setters privés dans les classes RealmObject.

Il est également possible d'implémenter RealmModel au lieu d'étendre RealmObject , si la classe est également annotée avec @RealmClass .

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

Dans ce cas, les méthodes telles que person.deleteFromRealm() ou person.addChangeListener() sont remplacées par RealmObject.deleteFromRealm(person) et RealmObject.addChangeListener(person) .

Les limitations sont que par un RealmObject , seul RealmObject peut être étendu, et il n'y a pas de prise en charge pour transient champs final , volatile et transient .

Il est important qu'une classe RealmObject gérée ne puisse être modifiée que dans une transaction. Un RealmObject géré ne peut pas être transmis entre les threads.

Liste de primitives (RealmList )

Realm ne prend actuellement pas en charge le stockage d'une liste de primitives. C'est sur leur liste de tâches ( numéro GitHub n ° 575 ), mais pour le moment, voici une solution de contournement.

Créez une nouvelle classe pour votre type primitif, cela utilise Integer, mais changez-le pour ce que vous voulez stocker.

public class RealmInteger extends RealmObject {
    private int val;

    public RealmInteger() {
    }

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

    // Getters and setters
}

Vous pouvez maintenant utiliser ceci dans votre RealmObject .

public class MainObject extends RealmObject {

    private String name;
    private RealmList<RealmInteger> ints;

    // Getters and setters
}

Si vous utilisez GSON pour remplir votre RealmObject , vous devrez ajouter un adaptateur de type personnalisé.

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

essayer avec les ressources

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
}

Le Try with resources ne peut être utilisé qu'à partir de KITKAT (minSDK 19)

Requêtes triées

Pour trier une requête, au lieu d'utiliser findAll() , vous devez utiliser findAllSorted() .

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

Remarque:

sort() renvoie un tout nouveau RealmResults trié, mais une mise à jour de ce RealmResults le réinitialisera. Si vous utilisez sort() , vous devez toujours le trier de nouveau dans votre RealmChangeListener , supprimer le RealmChangeListener des anciens RealmResults et l'ajouter au nouveau RealmResults . L'utilisation de sort() sur un RealmResults renvoyé par une requête asynchrone non encore chargée échouera.

findAllSorted() retournera toujours les résultats triés par le champ, même s'il est mis à jour. Il est recommandé d'utiliser findAllSorted() .

Requêtes asynchrones

Chaque méthode de requête synchrone (telle que findAll() ou findAllSorted() ) a un équivalent asynchrone ( findAllAsync() / findAllSortedAsync() ).

Les requêtes asynchrones déchargent l'évaluation de RealmResults sur un autre thread. Pour recevoir ces résultats sur le thread en cours, le thread en cours doit être un thread looper (lisez: les requêtes asynchrones ne fonctionnent généralement que sur le thread d'interface utilisateur).

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

Utiliser le domaine avec RxJava

Pour les requêtes, Realm fournit la méthode realmResults.asObservable() . L'observation des résultats n'est possible que sur les threads de boucle (généralement le thread d'interface utilisateur).

Pour que cela fonctionne, votre configuration doit contenir les éléments suivants:

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

Ensuite, vous pouvez utiliser vos résultats comme observable.

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

Pour les requêtes asynchrones, vous devez filtrer les résultats par isLoaded() afin de recevoir un événement uniquement lorsque la requête a été exécutée. Ce filter() n'est pas nécessaire pour les requêtes synchrones ( isLoaded() renvoie toujours la valeur true pour les requêtes de synchronisation).

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

Pour les écritures, vous devez soit utiliser la méthode executeTransactionAsync() , soit ouvrir une instance Realm sur le thread d'arrière-plan, exécuter la transaction de manière synchrone, puis fermer l'instance Realm.

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

Utilisation de base

Mise en place d'une instance

Pour utiliser Realm, vous devez d'abord en obtenir une instance. Chaque instance de domaine correspond à un fichier sur le disque. La manière la plus simple d’obtenir une instance est la suivante:

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

La méthode Realm.getInstance() crée le fichier de base de données s'il n'a pas été créé, sinon ouvre le fichier. L'objet RealmConfiguration contrôle tous les aspects de la création d'un domaine, qu'il s'agisse d'une base de données inMemory() , du nom du fichier Realm, si le domaine doit être effacé si une migration est nécessaire, des données initiales, etc.

Notez que les appels à Realm.getInstance() sont comptés (chaque appel incrémente un compteur) et le compteur est décrémenté lorsque realm.close() est appelé.

Fermer une instance

Sur les threads d'arrière-plan, il est très important de fermer les instances Realm une fois qu'elles ne sont plus utilisées (par exemple, la transaction est terminée et l'exécution du thread se termine). Si vous ne fermez pas toutes les instances de Realm sur le thread d'arrière-plan, cela entraîne un épinglage de la version et peut entraîner une augmentation importante de la taille du fichier.

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

Il convient de noter qu'au-dessus du niveau 19 de l'API, vous pouvez remplacer ce code par ceci:

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

Des modèles

La prochaine étape serait la création de vos modèles. Ici, une question pourrait être posée: "Qu'est-ce qu'un modèle?". Un modèle est une structure qui définit les propriétés d'un objet stocké dans la base de données. Par exemple, dans ce qui suit, nous modélisons un livre.

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

Notez que vos modèles doivent étendre la classe RealmObject. La clé primaire est également spécifiée par l'annotation @PrimaryKey . Les clés primaires peuvent être nulles, mais un seul élément peut avoir la valeur null tant que clé primaire. Vous pouvez également utiliser l'annotation @Ignore pour les champs à ne pas @Ignore sur le disque:

@Ignore
private String isbn;

Insérer ou mettre à jour des données

Pour stocker un objet livre dans votre instance de base de données Realm, vous pouvez d'abord créer une instance de votre modèle, puis la stocker dans la base de données via la méthode copyToRealm . Pour créer ou mettre à jour, vous pouvez utiliser copyToRealmOrUpdate . (Une alternative plus rapide est la 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);
    }
});

Notez que toutes les modifications apportées aux données doivent avoir lieu dans une transaction. Une autre façon de créer un objet consiste à utiliser le modèle suivant:

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

Interroger la base de données

  • Tous les livres:

    RealmResults<Book> results = realm.where(Book.class).findAll();
    
  • Tous les livres ayant un identifiant supérieur à 10:

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

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

Supprimer un objet

Par exemple, nous voulons supprimer tous les livres de 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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow