Question

Tout le monde a de bons conseils sur la façon de mettre en œuvre un à plusieurs pour la cartographie à l'aide SQLite ContentProvider? Si vous regardez Uri ContentProvider#insert(Uri, ContentValues) vous pouvez voir qu'il a ContentValues qui contient des données param à insérer. Le problème est que dans sa ContentValues de mise en œuvre actuelle ne supporte pas la méthode de put(String, Object) et la classe est finale, donc je ne peux pas l'étendre. Pourquoi est-il un problème? Voici vient ma conception:

J'ai 2 tables qui sont un à plusieurs. Pour les représenter dans le code que j'ai 2 objets de modèle. 1er représente le dossier principal et a un champ qui est une liste d'instances de 2e objet. Maintenant, j'ai une méthode d'assistance dans l'objet modèle # 1 qui retourne ContentValues généré sur l'objet courant. Il est trivial de remplir un champs primitifs avec des méthodes surchargées ContentValues#put mais je suis hors de la chance pour la liste. Donc, actuellement depuis ma 2ème ligne de la table est une seule valeur de chaîne que je produis une chaîne délimitée par des virgules qui je Reparse à String [] à l'intérieur ContentProvider#insert. Cela se sent dégueu, alors peut-être que quelqu'un peut laisser entendre la façon dont il peut être fait de façon plus propre.

Voici un code. Tout d'abord de la classe modèle:

public ContentValues toContentValues() {
    ContentValues values = new ContentValues();
    values.put(ITEM_ID, itemId);
    values.put(NAME, name);
    values.put(TYPES, concat(types));
    return values;
}

private String concat(String[] values) { /* trivial */}

et est ici aminci version de méthode ContentProvider#insert

public Uri insert(Uri uri, ContentValues values) {
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    db.beginTransaction();
    try {
        // populate types
        String[] types = ((String)values.get(Offer.TYPES)).split("|");
        // we no longer need it
        values.remove(Offer.TYPES);
        // first insert row into OFFERS
        final long rowId = db.insert("offers", Offer.NAME, values);
        if (rowId > 0 && types != null) {
            // now insert all types for the row
            for (String t : types) {
                ContentValues type = new ContentValues(8);
                type.put(Offer.OFFER_ID, rowId);
                type.put(Offer.TYPE, t);
                // insert values into second table
                db.insert("types", Offer.TYPE, type);
            }
        }
        db.setTransactionSuccessful();
        return ContentUris.withAppendedId(Offer.CONTENT_URI, rowId);
    } catch (Exception e) {
        Log.e(TAG, "Failed to insert record", e);
    } finally {
        db.endTransaction();
    }

}
Était-ce utile?

La solution

Je pense que vous êtes à la recherche du mauvais côté de l'un à plusieurs.

Jetez un coup d'oeil au fournisseur de contenu ContactsContract, par exemple. Les contacts peuvent avoir de nombreuses adresses e-mail, de nombreux numéros de téléphone, etc. La façon dont est accompli est par des insertions / mises à jour / suppressions de « beaucoup » côté. Pour ajouter un nouveau numéro de téléphone, vous insérez un nouveau numéro de téléphone, fournissant un identifiant du contact dont le numéro de téléphone se rapporte.

Vous feriez la même chose si vous aviez une base de données SQLite simple avec aucun fournisseur de contenu. Un à de nombreuses relations dans les bases de données relationnelles sont réalisées via des mises à jour / insertions / suppressions sur une table pour le côté « plusieurs », ayant chacun une clé étrangère vers le côté « un ».

Maintenant, du point de vue OO, ce n'est pas idéal. Nous vous invitons à créer des objets wrapper style ORM (Hibernate pense) qui vous permettent de manipuler une collection d'enfants du côté « un ». Une classe de collecte suffisamment intelligent peut alors tourner autour et de synchroniser la table « beaucoup » pour correspondre. Cependant, ceux-ci ne sont pas nécessairement trivial à mettre en œuvre correctement.

Autres conseils

Vous pouvez utiliser ContentProviderOperations pour cela.

Ils sont essentiellement des opérations en vrac avec la possibilité de rétro-référence aux identificateurs générés pour les rangées de parents.

Comment ContentProviderOperations peut être utilisé pour un à plusieurs design est très bien expliqué dans cette réponse: Quels sont la sémantique de withValueBackReference

Je vais répondre à ma propre question. J'étais sur la bonne voie d'avoir deux tables et deux objets de modèle. Ce qui manquait et ce qui m'a confondu était que je voulais insérer directement des données complexes par ContentProvider#insert en un seul appel. C'est faux. ContentProvider devrait créer et maintenir ces deux tableaux, mais la décision sur la table à utiliser doit être dicté par le paramètre de ContentProvider#insert Uri. Il est très pratique d'utiliser des méthodes ContentResolver et ajouter comme « addFoo » à l'objet modèle. Une telle méthode prendrait paramètre ContentResolver et à la fin voici la séquence d'insérer un enregistrement complexe:

  1. Insérer un enregistrement parent par ContentProvider#insert et obtenir ID d'enregistrement
  2. pour chaque enfant une pièce d'identité des parents (clé foregn) et utiliser ContentProvider#insert avec différents Uri pour insérer des enregistrements enfants

Donc, la seule question qui reste est de savoir comment enveloppe le code ci-dessus dans la transaction?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top