Android: conception SQLite one-to-many
Quelqu'un a de bons conseils sur la façon de mettre en œuvre un-à-plusieurs de cartographie pour SQLite
à l'aide de ContentProvider
? Si vous regardez Uri ContentProvider#insert(Uri, ContentValues)
vous pouvez voir qu'il a ContentValues
param qui contient les données à insérer. Le problème est que dans son implémentation actuelle ContentValues
ne prend pas en charge put(String, Object)
et méthode de classe est finale donc je ne peut pas le prolonger. Pourquoi c'est un problème? Voici mon dessin:
J'ai 2 tables qui sont dans l'une-à-plusieurs relations. Pour représenter ces dans le code j'ai 2 objets de modèle. 1er représente l'enregistrement principal et dispose d'un champ est une liste de 2e instances de l'objet. Maintenant, j'ai une méthode d'assistance dans le modèle d'objet #1 qui renvoie ContentValues
générés à partir de l'objet courant. Il est trivial pour remplir une primitive champs avec ContentValues#put
méthodes surchargées, mais je suis hors de la chance pour la liste. Donc, actuellement, depuis mon 2ème ligne du tableau est juste une seule Chaîne de valeur-je générer une Chaîne délimitée par des virgules, qui puis-je d'analyse String[] à l'intérieur de ContentProvider#insert
. Qui se sent sale, peut-être que quelqu'un indicateur de la façon dont cela peut être fait en nettoyant la mode.
Voici un peu de code. D'abord à partir de la classe de 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 voici une version allégée de ContentProvider#insert
méthode
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();
}
}
source d'informationauteur Bostone
Vous devez vous connecter pour publier un commentaire.
Je pense que vous êtes en train de regarder du mauvais côté de l'un-à-plusieurs relations.
Prendre un coup d'oeil à la
ContactsContract
fournisseur de contenu, par exemple. Les Contacts peuvent avoir plusieurs adresses e-mail, le nombre de numéros de téléphone, etc. La façon d'y parvenir est de faire des insertions/mises à jour/efface sur le "beaucoup" côté. Pour ajouter un nouveau numéro de téléphone, vous devez insérer un nouveau numéro de téléphone, de fournir un ID de contact dont le numéro de téléphone de porte.Vous devez faire de même si vous aviez une simple base de données SQLite avec aucun fournisseur de contenu. Un-à-plusieurs relations dans les bases de données relationnelles sont réalisées via des insertions, mises à jour et des suppressions sur une table pour le côté "plusieurs", ayant chacun une clé étrangère pour le côté "un".
Maintenant, à partir d'un OO point de vue, ce n'est pas l'idéal. Vous êtes les bienvenus pour créer ORM-style wrapper objets (pensez à Hibernate) qui vous permettent de manipuler une collection d'enfants à partir du côté "un". Suffisamment intelligent de la classe de collection peut alors tourner autour et de synchroniser la table "plusieurs" de match. Cependant, ce ne sont pas nécessairement trivial à mettre en œuvre correctement.
Vous pouvez utiliser
ContentProviderOperations
pour cela.Ils sont essentiellement des opérations en bloc avec la possibilité de retour de référence pour les identifiants générés pour les lignes parent.
Comment
ContentProviderOperations
peut être utilisé pour un un-à-plusieurs design est très bien expliqué dans cette réponse: Ce sont la sémantique de withValueBackReference?Donc je vais répondre à ma propre question. J'étais sur la bonne voie avec le fait d'avoir deux tables et deux objets de modèle. Ce qui manque et ce qui me confond était que je voulais insérer directement des données complexes par le biais de
ContentProvider#insert
en un seul appel. Ce qui est faux.ContentProvider
devrait créer et maintenir ces deux tables, mais la décision sur la table qui doit être dicté par le paramètre Uri deContentProvider#insert
. Il est très pratique à utiliser ContentResolver et ajouter des méthodes telles que "addFoo" pour le modèle objet. Une telle méthode permettrait de prendre ContentResolver paramètre et à la fin, voici la séquence à insérer un enregistrement complexe:ContentProvider#insert
et d'obtenir l'id de l'enregistrementContentProvider#insert
avec Uri différent pour insérer des enregistrements enfantsDonc, la seule question qui reste est comment enveloppe le code ci-dessus dans la transaction?