Pregunta

Cualquier persona tiene buenos consejos sobre cómo poner en práctica uno-a-muchos de mapeo para SQLite usando ContentProvider? Si nos fijamos en Uri ContentProvider#insert(Uri, ContentValues) se puede ver que se ha ContentValues parámetro que contiene los datos a insertar. El problema es que en su actual ContentValues aplicación no es compatible con el método put(String, Object) y la clase es final así que no puedo extenderla. ¿Por qué es un problema? Aquí viene mi diseño:

Tengo 2 tablas que se encuentran en uno-a-muchos relación. Para representar estos valores en código que tengo 2 objetos del modelo. Primero representa el registro principal y tiene un campo que es una lista de instancias de objetos 2do. Ahora tengo un método de ayuda en el modelo de objetos # 1 que devuelve ContentValues genera fuera del objeto actual. Es trivial para rellenar un campo con métodos primitivos ContentValues#put sobrecargado, pero estoy fuera de suerte para la lista. Así que actualmente ya que mi segunda fila de la tabla es sólo un único valor de cadena genero una cadena delimitada por comas que luego de reanálisis a String [] en el interior ContentProvider#insert. Eso se siente asco, así que tal vez alguien puede sugerir la forma en que se puede hacer de una manera más limpia.

Aquí hay algo de código. En primer lugar de la clase del modelo:

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

Y aquí está adelgazado versión del método 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();
    }

}
¿Fue útil?

Solución

Creo que usted está buscando en el lado equivocado de la relación de uno a muchos.

Tome una mirada en el proveedor de contenidos ContactsContract, por ejemplo. Los contactos pueden tener muchas direcciones de correo electrónico, muchos números de teléfono, etc. La forma en que se logra es haciendo inserciones / actualizaciones / eliminaciones en la lado "varios" . Para añadir un nuevo número de teléfono, se inserta un nuevo número de teléfono, proporcionando una identificación del contacto para el cual el número de teléfono pertenece.

Se podría hacer lo mismo si tuviera una base de datos SQLite llano con ningún proveedor de contenido. Uno-a-muchas relaciones en bases de datos relacionales se logran a través de inserciones / actualizaciones / eliminaciones en una mesa para el lado "varios", cada uno con una clave externa de nuevo al lado "uno".

Ahora bien, desde un punto de vista orientado a objetos, esto no es lo ideal. Usted es bienvenido para crear objetos envolventes de tipo ORM Hibernate (piensa) que le permiten manipular una colección de los niños desde el lado "uno". Una clase de colección lo suficientemente inteligente puede entonces dar la vuelta y sincronizar la tabla "varios" de igualar. Sin embargo, éstas no son necesariamente trivial para aplicar correctamente.

Otros consejos

Puede utilizar ContentProviderOperations para esto.

Son básicamente las operaciones a granel con la capacidad de hacer una copia de referencia a los identificadores generados por filas primarias.

¿Cómo ContentProviderOperations se puede utilizar para un diseño de uno a muchos está muy bien explicado en esta respuesta: ¿Cuáles son la semántica de withValueBackReference?

Así que voy a responder a mi propia pregunta. Yo estaba en el camino correcto con tener dos mesas y dos objetos del modelo. Lo que faltaba y lo que me confunde es que quería insertar directamente datos complejos a través ContentProvider#insert en una sola llamada. Esto está mal. ContentProvider debe crear y mantener estas dos mesas pero la decisión sobre la que debe ser dictado por el parámetro de Uri ContentProvider#insert mesa de usar. Es muy conveniente utilizar ContentResolver y añadir métodos tales como "addFoo" al objeto modelo. Tal método sería tomar parámetro ContentResolver y al final aquí son la secuencia para insertar un registro complejo:

  1. Insertar registro padre a través ContentProvider#insert y obtener registro id
  2. Por cada niño proporcionar una identificación con el padre (clave foregn) y utilizar ContentProvider#insert con diferentes Uri para insertar registros secundarios

Así que la única cuestión pendiente es cómo envuelva el código anterior en la transacción?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top