[JAVA] Sauvegarde grammaire-RealmObject de transformation générique, ce que j'ai pensé parce que je suis fatigué d'écrire RealmMigration pendant le développement mais que je veux reprendre les données

Le début des choses

Je crée actuellement une application Android en utilisant Realm, mais je veux souvent jouer avec la configuration de la base de données. ↓ Cependant, même s'il s'agit d'une application en phase de débogage, il est gênant de perdre des données car j'ai déjà commencé à utiliser l'application pratiquement. ↓ La seule chose à faire est d'augmenter le nombre de colonnes, il est donc difficile de vérifier et d'écrire Realm Migration une par une. ↓ Je pense qu'il est préférable de jsoniser la liste de RealmObjects, de la sauvegarder dans Préférences, de la restaurer dans RealmObject et de la mettre en place. ↓ essayons

façon

Pour le moment

ItemRealmObject

Ceci est une classe d'objets ordinaire

Classe d'accès Créez une classe appelée RealmHelper pour y accéder

public class ItemRealmHelper extends AbstractRealmHelper {

    public static void insertOneShot(ItemRealmObject itemRealmObject) {
        executeTransactionOneShot(insertTransaction(itemRealmObject));
    }

    private static Realm.Transaction insertTransaction(final ItemRealmObject itemRealmObject) {
        return new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                realm.insertOrUpdate(itemRealmObject);
            }
        };
    }

    @Override
    public void upsert(final ItemRealmObject item) {
        item.updateDate = new Date();
        executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                realm.copyToRealmOrUpdate(item);
            }
        });
    }

    @Override
    public RealmResults<ItemRealmObject> findAll() {
        return mRealm.where(ItemRealmObject.class).findAll();
    }
}
public abstract class AbstractRealmHelper<T extends RealmObject> {

    protected final Realm mRealm;

    public AbstractRealmHelper() {
        mRealm = getRealm();
    }

    static Realm getRealm() {
        return Realm.getInstance(new RealmConfiguration.Builder().deleteRealmIfMigrationNeeded().build());
    }

    protected static void executeTransactionOneShot(Realm.Transaction transaction) {
        Realm realm = getRealm();
        realm.executeTransaction(transaction);
        realm.close();
    }

    public abstract void upsert(T t);

    public abstract RealmResults<T> findAll();

    protected void executeTransaction(Realm.Transaction transaction) {
        mRealm.executeTransaction(transaction);
    }

    public void destroy() {
        mRealm.close();
    }
}

"Au début, je viens de le remettre en json ~", je me sentais léger.

Lire la documentation de Realm https://realm.io/jp/docs/java/latest/#gson

GSON est une bibliothèque Google qui sérialise / désérialise JSON. GSON ne nécessite aucun paramètre spécial et peut être utilisé avec Realm tel quel.

Je vois. Tu peux y aller.

Tout d'abord, je veux l'enregistrer dans les Préférences, je vais donc le sérialiser immédiatement.

new Gson().toJson(RealmObject);

Puis

StackOverFlowError

Ce n'est pas bien ... Jetez à nouveau un œil à la documentation.

En ce qui concerne la sérialisation, les objets Realm ne peuvent pas être sérialisés avec les valeurs par défaut GSON. Cela est dû au fait que GSON n'utilise pas getter / setter et fait directement référence aux variables d'instance. Pour que la sérialisation JSON des objets Realm à l'aide de GSON fonctionne correctement, vous devez écrire un JsonSerializer et un TypeAdapter personnalisés pour chaque modèle. Le code de ce Gist montre comment les écrire.

Dois-je écrire quelque chose ... Je ne sais pas. La désérialisation peut-elle être effectuée pour RealmObject? Si···.

J'ai donc décidé de créer une classe de modèle qui n'est pas le même RealmObject et de la convertir en Json. Créez une classe de modèle avec le même nom de champ et définissez-la de sorte que vous puissiez entrer une valeur dans chaque champ si vous transmettez RealmObject au constructeur. Créez une liste de classes de modèle en transformant les RealmResults récupérés par findAll de RealmHelper avec une instruction for, convertissez-la en Json et enregistrez-la dans Preferences.

    void save() {
        writeData(PrefKey.itemRealm, new ItemRealmHelper());
        writeData(PrefKey.priceRealm, new PriceRealmHelper());
    }

    private <T extends RealmObject> void writeData(Enum key, AbstractRealmHelper<T> helper) {
        RealmResults<T> results = helper.findAll();
        List<Object> list = new ArrayList<>();
        for (T t : results) {
            list.add(getModel(t));
        }
        PreferenceUtils.writeValue(getContext(), key, JsonUtils.toJson(list));
        helper.destroy();
    }

    //Ajoutez une description ici quand elle augmente
    private Object getModel(Object o) {
        if (o instanceof ItemRealmObject) {
            return new ItemModel((ItemRealmObject) o);
        } else if (o instanceof PriceRealmObject){
            return new PriceModel((PriceRealmObject) o);
        }
        throw new InternalError();
    }

(Ce code était un peu plus compliqué à l'origine car je l'ai refactoré après avoir créé la méthode de chargement ci-dessous)

... Eh bien, la sauvegarde s'est bien déroulée!

Eh bien, tout ce que vous avez à faire est de le remettre

Écrivez le code pour le récupérer

ItemRealmHelper itemHelper = new ItemRealmHelper();
String json = PreferenceUtils.readValue(getContext(), PrefKey.itemRealm, "");
List<ItemRealmObject> itemModelList = new Gson().fromJson(json, new TypeToken<List<ItemRealmObject>>() {}.getType());
for (ItemRealmObject itemModel : itemModelList) {
    itemHelper.upsert(itemModel);
}

Oh, j'ai pu y aller.

Cependant, j'y pense ici. "Je crée des RealmObjects, donc si je l'écris honnêtement, ce n'est pas facile pour le code d'augmenter tel quel, et c'est difficile de copier et de changer le type à chaque fois ..."

//Si vous l'écrivez honnêtement, cela augmentera ...
void load() {
    ItemRealmHelper itemHelper = new ItemRealmHelper();
    String json = PreferenceUtils.readValue(getContext(), PrefKey.itemRealm, "");
    List<ItemRealmObject> itemModelList = new Gson().fromJson(json, new TypeToken<List<ItemRealmObject>>() {}.getType());
    for (ItemRealmObject itemModel : itemModelList) {
        itemHelper.upsert(itemModel);
    }
    itemHelper.destroy();
    
    Item2RealmHelper item2Helper = new Item2RealmHelper();
    String json2 = PreferenceUtils.readValue(getContext(), PrefKey.item2Realm, "");
    List<Item2RealmObject> item2ModelList = new Gson().fromJson(json, new TypeToken<List<Item2RealmObject>>() {}.getType());
    for (Item2RealmObject item2Model : item2ModelList) {
        item2Helper.upsert(item2Model);
    }
    item2Helper.destroy();
}
// ...Il augmente à chaque fois qu'il augmente

En disant ...

Les génériques entrent en jeu

Je l'ai fait en essayant d'en faire une méthode avec upsert.

    private <T extends RealmObject> void upsert(Enum key, TypeToken<T> typeToken, final AbstractRealmHelper<T> helper) {
        List<T> list = new Gson().fromJson(PreferenceUtils.readValue(getContext(), key, ""), new TypeToken<List<T>>() {}.getType());
        for (T t : list) {
            helper.upsert(t);
        }
        helper.destory();
    }

D'accord, je l'ai bien écrit. Alors exécutez.

ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast

Qu'est-ce que c'est ...

Quand j'ai cherché, un article est sorti.

Je suis accro à la conversion de chaînes JSON en objets génériques avec Gson http://osa030.hatenablog.com/entry/2015/10/20/182439

En poursuivant votre lecture,

Le résultat de Gson fromJson peut être décodé jusqu'à la classe Hoge, mais la valeur est "com.google.gson.internal.LinkedTreeMap" au lieu de la classe Fuga.

En d'autres termes, il s'agit d'un type List, mais il semble que la destination soit LinkedTreeMap.

Once your .java source file is compiled, the type parameter is obviously thrown away. Since it is not known at compile time, it cannot be stored in bytecode, so it's erased and Gson can't read it.

En bref "" disparaît au moment de la construction, il n'a donc pas l'air dynamique. C'est pourquoi je suis arrivé à Linked Tree Map avec gentillesse.

Il y avait un lien StackOverFlow, alors jetez un œil.

Java Type Generic as Argument for GSON https://stackoverflow.com/questions/14139437/java-type-generic-as-argument-for-gson/14139700#14139700

C'est un exemple que je souhaite convertir en List, donc il me correspond.

Copiez ListOfSomething de StackOverFlow et utilisez-le.

    private <T extends RealmObject> void upsert(Enum key, Class<T> clazz, final AbstractRealmHelper<T> helper) {
        List<T> list = new Gson().fromJson(PreferenceUtils.readValue(getContext(), key, ""), new ListOfSomething<>(clazz));
        for (T t : list) {
            helper.upsert(t);
        }
        helper.destory();
    }

Oh. l'a fait. C'est incroyable.

En regardant le contenu, getRawType renvoie List.class de manière fixe. Donc je comprends que la personne dans le premier article a créé la classe GenericOf. Puisque c'est un gros problème, je vais le changer (?) Compte tenu de la polyvalence.

    private <T extends RealmObject> void upsert(Enum key, Class<T> clazz, final AbstractRealmHelper<T> helper) {
        List<T> list = new Gson().fromJson(PreferenceUtils.readValue(getContext(), key, ""), new GenericOf<>(List.class, clazz));
        for (T t : list) {
            Footprint.fields(t);
            helper.upsert(t);
        }
        helper.destory();
    }

C'était le plus rafraîchissant.

Maintenant, dans la méthode de chargement, il suffit d'appeler upsert, donc il passe de 7 lignes à 1. Par exemple, même s'il y a 2 RealmObjects, ajoutez simplement une ligne à la fois!

void load() {
    upsert(PrefKey.itemRealm, ItemRealmObject.class, new ItemRealmHelper());
    upsert(PrefKey.item2Realm, Item2RealmObject.class, new Item2RealmHelper());
}

C'était rafraîchissant et facile, et j'étais heureux.

Résumé

    void save() {
        writeData(PrefKey.itemRealm, new ItemRealmHelper());
        writeData(PrefKey.priceRealm, new PriceRealmHelper());
    }

    private <T extends RealmObject> void writeData(Enum key, AbstractRealmHelper<T> helper) {
        RealmResults<T> results = helper.findAll();
        List<Object> list = new ArrayList<>();
        for (T t : results) {
            list.add(getModel(t));
        }
        PreferenceUtils.writeValue(getContext(), key, JsonUtils.toJson(list));
        helper.destroy();
    }

    //Ajoutez une description ici quand elle augmente
    private Object getModel(Object o) {
        if (o instanceof ItemRealmObject) {
            return new ItemModel((ItemRealmObject) o);
        } else if (o instanceof PriceRealmObject){
            return new PriceModel((PriceRealmObject) o);
        }
        throw new InternalError();
    }
    void load() {
        upsert(PrefKey.itemRealm, ItemRealmObject.class, new ItemRealmHelper());
        upsert(PrefKey.item2Realm, Item2RealmObject.class, new Item2RealmHelper());
    }

    private <T extends RealmObject> void upsert(Enum key, Class<T> clazz, final AbstractRealmHelper<T> helper) {
        List<T> list = new Gson().fromJson(PreferenceUtils.readValue(getContext(), key, ""), new GenericOf<>(List.class, clazz));
        for (T t : list) {
            helper.upsert(t);
        }
        helper.destory();
    }

finalement

J'étais heureux de pouvoir utiliser TypeToken pour spécifier une classe qui inclut une liste dans Gson, mais quand j'y ai impliqué des génériques, j'ai trouvé que j'avais besoin d'un ParameterizedType. Les deux Gson et Generics sont encore profonds. Que faire si vous voulez prendre le type Liste de Liste avec des génériques? J'ai pensé, mais cela semble dangereux alors j'ai arrêté de penser. (Est-il correct de définir les deux arguments GenericsOf sur TypeToken ...?)

Si cela prend autant, pourquoi ne pas écrire JsonSerializer et TypeAdapter afin que RealmObject puisse être sérialisé? Je pensais que c'était un secret. De plus, n'était-il pas plus rapide d'écrire Migration en premier lieu? C'est plus secret. C'est bien parce que j'ai appris ...

Recommended Posts

Sauvegarde grammaire-RealmObject de transformation générique, ce que j'ai pensé parce que je suis fatigué d'écrire RealmMigration pendant le développement mais que je veux reprendre les données
C'était une vie que je voulais réinitialiser le compteur associatif thread-safe
J'ai été beaucoup facturé par AWS pendant le développement sur rails, mais ...
Quand j'ai voulu créer une méthode pour Premium Friday, c'était déjà dans l'API standard Java 8