Frage

Jeder hat gute Ratschläge zur Implementierung von Eins-zu-Viele-Mapping für SQLite Verwendung ContentProvider? Wenn Sie sich ansehen Uri ContentProvider#insert(Uri, ContentValues) Sie können sehen, dass es hat ContentValues Param, der Daten enthält, die einfügen sollen. Das Problem ist, dass in seiner aktuellen Implementierung ContentValues unterstützt nicht put(String, Object) Methode und Klasse sind endgültig, daher kann ich sie nicht erweitern. Warum ist es ein Problem? Hier kommt mein Design:

Ich habe 2 Tabellen, die sich in einer eins-zu-Viele-Beziehung befinden. Um diese in Code darzustellen, habe ich 2 Modellobjekte. 1st repräsentiert den Hauptdatensatz und verfügt über ein Feld, das eine Liste der 2. Objektinstanzen ist. Jetzt habe ich eine Helfermethode im Modellobjekt Nr. 1, die zurückgibt ContentValues vom aktuellen Objekt generiert. Es ist trivial, primitive Felder mit ContentValues#put Überladene Methoden, aber ich habe kein Glück für die Liste. Da meine 2. Tabellenzeile derzeit nur ein einzelner String -Wert ist, generiere ich eine Comma -Abgrenzungsfolge, die ich dann zu String [] in Inside repariere ContentProvider#insert. Das fühlt sich juck an, also kann jemand vielleicht darauf hinweisen, wie es auf sauberer Weise gemacht werden kann.

Hier ist ein Code. Zuerst aus der Modellklasse:

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 */}

Und hier ist die Version von verklemmt ContentProvider#insert Methode

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

}
War es hilfreich?

Lösung

Ich denke, Sie schauen auf das falsche Ende der Eins-zu-Viele-Beziehung.

Schauen Sie sich das an die ContactsContract Inhaltsanbieter zum Beispiel. Kontakte können viele E -Mail -Adressen, viele Telefonnummern usw. haben "viele" Seite. Um eine neue Telefonnummer hinzuzufügen, geben Sie eine neue Telefonnummer ein und geben eine ID des Kontakts an, für die sich die Telefonnummer bezieht.

Sie würden dasselbe tun, wenn Sie eine einfache SQLite -Datenbank ohne Inhaltsanbieter hätten. Eins-zu-Viele-Beziehungen in relationalen Datenbanken werden über Einfügungen/Updates/Löschungen auf einer Tabelle für die Seite "Viele" erreicht, wobei jeder einen Fremdschlüssel auf der "One" -Seite zurückgibt.

Aus OO -Sicht ist dies nicht ideal. Sie können herzlich eingestuft werden, um Wrapper-Objekte im Orm-Stil zu erstellen (denken Sie Hibernate), mit denen Sie eine Sammlung von Kindern von der "One" -Seite manipulieren können. Eine ausreichend intelligente Sammlungsklasse kann sich dann umdrehen und die übereinstimmende "Viele" -Tabelle synchronisieren. Diese sind jedoch nicht unbedingt trivial, um ordnungsgemäß umzusetzen.

Andere Tipps

Sie können verwenden ContentProviderOperations dafür.

Sie sind im Grunde genommen Bulk-Operationen mit der Fähigkeit, die für Elternreihen generierten Kennungen zu beenden.

Wie ContentProviderOperations Kann für ein Eins-zu-viele-Design verwendet werden, wird in dieser Antwort sehr gut erklärt: Was ist die Semantik von With ValueBackReference?

Also werde ich meine eigene Frage beantworten. Ich war auf dem richtigen Weg mit zwei Tischen und zwei Modellobjekten. Was fehlte und was mich verwirrte, war, dass ich direkt komplexe Daten durch einfügen wollte ContentProvider#insert in einem einzigen Anruf. Das ist falsch. ContentProvider Sollte diese beiden Tabellen erstellen und verwalten, aber die Entscheidung, welche Tabelle verwendet werden soll, sollte durch den URI -Parameter von diktiert werden ContentProvider#insert. Es ist sehr bequem, ContentResolver zu verwenden und Methoden wie "Addfoo" zum Modellobjekt hinzuzufügen. Eine solche Methode würde den ContentResolver -Parameter aufnehmen und am Ende finden Sie hier die Reihenfolge, um einen komplexen Datensatz einzufügen:

  1. Fügen Sie den übergeordneten Datensatz durch ContentProvider#insert und Record ID erhalten
  2. Pro Jedes Kind geben die übergeordnete ID (Vorgesetzte) und verwenden Sie die Verwendung ContentProvider#insert mit verschiedenen URI, um untergeordnete Datensätze einzulegen

Die einzige verbleibende Frage ist also, wie der obige Code in der Transaktion umhüllt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top