Domanda

Qualcuno ha buoni consigli su come implementare uno-a-molti mapping per SQLite utilizzando ContentProvider? Se si guarda a Uri ContentProvider#insert(Uri, ContentValues) si può vedere che ha ContentValues param che contiene i dati da inserire. Il problema è che nella sua attuazione ContentValues attuale non supporta metodo put(String, Object) e categoria è definitiva quindi non posso estenderlo. Perché è un problema? Ecco che arriva il mio progetto:

Ho 2 tavoli che si trovano in uno-a-molti. Per rappresentare questi in codice che ho 2 oggetti del modello. 1 ° rappresenta il record principale e dispone di un campo che è un elenco di istanze di oggetti 2 °. Ora ho un metodo di supporto nel modello a oggetti # 1 che restituisce ContentValues generato fuori l'oggetto corrente. E 'banale per popolare un campo primitivo con metodi ContentValues#put sovraccarico ma sono fuori di fortuna per la lista. Quindi al momento dato che il mio 2 ° riga della tabella è solo un singolo valore String ho generare una stringa delimitata da virgole che poi ho di analisi per String [] all'interno ContentProvider#insert. Che si sente schifo, così forse qualcuno può suggerire come può essere fatto in modo più pulito.

Ecco un po 'di codice. In primo luogo dalla classe del modello:

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

ed ecco snellita versione del metodo 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();
    }

}
È stato utile?

Soluzione

Credo che si sta guardando dalla parte sbagliata della relazione uno-a-molti.

Date un'occhiata al fornitore di contenuti ContactsContract, per esempio. I contatti possono avere molti indirizzi e-mail, molti numeri di telefono, ecc Il modo che si compie è quello di eseguire inserti / aggiornamenti / eliminare il "molti" lato. Per aggiungere un nuovo numero di telefono, si inserisce un nuovo numero di telefono, che fornisce un ID del contatto per i quali il numero di telefono appartiene.

Si potrebbe fare lo stesso se si ha un database SQLite normale senza fornitore di contenuti. Uno-a-molti in database relazionali vengono raggiunti tramite inserti / aggiornamenti / cancella su un tavolo per il lato "molti", ciascuno con una chiave esterna di nuovo al lato "uno".

Ora, dal punto di vista OO, questo non è l'ideale. Siete invitati a creare oggetti wrapper ORM in stile (si pensi Hibernate) che consentono di manipolare una raccolta di bambini dal lato "uno". Una classe di raccolta sufficientemente-intelligente può poi girarsi e sincronizzare il "più" tabella per abbinare. Tuttavia, questi non sono necessariamente banali da implementare correttamente.

Altri suggerimenti

È possibile utilizzare ContentProviderOperations per questo.

sono operazioni fondamentalmente massa con la possibilità di eseguire riferimento agli identificatori generati per righe padre.

Come ContentProviderOperations può essere utilizzato per un design uno-a-molti è molto ben spiegato in questa risposta: Quali sono la semantica di withValueBackReference?

Quindi ho intenzione di rispondere alla mia domanda. Io ero sulla strada giusta con avere due tavoli e due oggetti del modello. Ciò che mancava e che cosa confuso mi era che volevo inserisco direttamente dati complessi attraverso ContentProvider#insert in una singola chiamata. Questo è sbagliato. ContentProvider dovrebbe creare e mantenere queste due tabelle, ma la decisione su quale tabella da utilizzare deve essere dettata dal parametro Uri di ContentProvider#insert. E 'molto comodo da usare ContentResolver e aggiungere metodi come "addFoo" all'oggetto modello. Tale metodo richiederebbe parametro ContentResolver e alla fine qui sono la sequenza per inserire un record complesso:

  1. Inserisci record padre attraverso ContentProvider#insert e ottenere il record id
  2. Per ogni bambino fornire ID genitore (chiave foregn) e utilizzare ContentProvider#insert con diversi Uri per inserire record figlio

Quindi l'unica domanda che rimane è come busta il codice di cui sopra in transazione?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top