Pregunta

La meta:actualizar la base de datos a partir de datos XML

El proceso:

  • Iniciar transacción
  • Borrar todas las filas existentes de las tablas
  • Por cada elemento principal del XML analizado insertar fila en la tabla principal y obtener PK
  • Por cada hijo del elemento principal. insertar registre en la segunda tabla proporcionando FK del paso anterior
  • Confirmar transacción

Cosas bastante estándar en cuanto a operaciones de base de datos.El problema es que las operaciones CRUD no se realizan dentro ContentProvider sino más bien usando ContentResolver entonces el inserto, por ejemplo, se ve así resolver.insert(CONTENT_URI, contentValues).La API ContentResolver no parece tener nada relacionado con la transacción y no puedo usarla. bulkInsert ya que estoy insertando en 2 tablas de forma intermitente (además quiero tener delete dentro de la transacción también).

Estaba pensando en registrar mi personalizado. ContentProvider como oyente usando registerContentObserver pero desde ContentResolver#acquireProvider Los métodos están ocultos ¿Cómo obtengo la referencia correcta?

¿No tengo suerte?

¿Fue útil?

Solución

He visto que en el código fuente de la aplicación Google I / O, que anulan las transacciones método ContentProvider y uso de applyBatch() dentro de ella. Así, se crea un lote de ContentProviderOperation s y luego llamar getContentResolver().applyBatch(uri_authority, batch).

Tengo la intención de utilizar este enfoque para ver cómo funciona. Tengo curiosidad por si alguien más ha probado.

Otros consejos

Es posible realizar inserciones de múltiples tablas basadas en transacciones de manera bastante limpia desde Android 2.1 usando ContentProviderOperation, como lo menciona kaciula.

Cuando crea el objeto ContentProviderOperation, puede llamar a .withValueBackReference(fieldName, refNr).Cuando la operación se aplica usando applyBatch, el resultado es que al objeto ContentValues ​​que se proporciona con la llamada insert() se le inyectará un número entero.El número entero se codificará con la cadena fieldName y su valor se recuperará del ContentProviderResult de una ContentProviderOperation aplicada previamente, indexada por refNr.

Consulte el ejemplo de código a continuación.En el ejemplo, se inserta una fila en la tabla1 y el ID resultante (en este caso "1") se usa como valor al insertar la fila en la tabla 2.Para abreviar, ContentProvider no está conectado a una base de datos.En ContentProvider, hay impresiones donde sería adecuado agregar el manejo de transacciones.

public class BatchTestActivity extends Activity {
    /** Called when the activity is first created. */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ArrayList<ContentProviderOperation> list = new
            ArrayList<ContentProviderOperation>();

        list.add(ContentProviderOperation.
            newInsert(BatchContentProvider.FIRST_URI).build());
        ContentValues cv = new ContentValues();
        cv.put("name", "second_name");
        cv.put("refId", 23);

        // In this example, "refId" in the contentValues will be overwritten by
        // the result from the first insert operation, indexed by 0
        list.add(ContentProviderOperation.
            newInsert(BatchContentProvider.SECOND_URI).
            withValues(cv).withValueBackReference("refId", 0).build());

        try {
            getContentResolver().applyBatch(
                BatchContentProvider.AUTHORITY, list);
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (OperationApplicationException e) {
            e.printStackTrace();
        }
    }
}

public class BatchContentProvider extends ContentProvider {

    private static final String SCHEME = "content://";
    public static final String AUTHORITY = "com.test.batch";

    public static final Uri FIRST_URI =
        Uri.parse(SCHEME + AUTHORITY + "/" + "table1");
    public static final Uri SECOND_URI =
        Uri.parse(SCHEME + AUTHORITY + "/" + "table2");


    public ContentProviderResult[] applyBatch(
        ArrayList<ContentProviderOperation> operations)
            throws OperationApplicationException {
        System.out.println("starting transaction");
        ContentProviderResult[] result;
        try {
            result = super.applyBatch(operations);
        } catch (OperationApplicationException e) {
            System.out.println("aborting transaction");
            throw e;
        }
        System.out.println("ending transaction");
        return result;
    }

    public Uri insert(Uri uri, ContentValues values) {
        // this printout will have a proper value when
        // the second operation is applied
        System.out.println("" + values);

        return ContentUris.withAppendedId(uri, 1);
    }

    // other overrides omitted for brevity
}

Muy bien, entonces esto no se queda sin rumbo:la única forma que se me ocurre es codificar startTransaction y endTransaction como solicitudes de consulta basadas en URL.Algo como ContentResolver.query(START_TRANSACTION, null, null, null, null).Luego en ContentProvider#query basado en la URL registrada, llame al inicio o final de la transacción

Puede obtener la aplicación del propio objeto proveedor de contenido (si está en el mismo proceso, pista: se puede controlar el proceso del proveedor con multiproceso = "true" o proceso = "" http://developer.android.com/guide/topics/manifest/provider-element.html ) usando ContentProviderClient.getLocalContentProvider (), que puede ser moldeado a su proveedor de aplicación que puede proporcionar una funcionalidad adicional como un reset () que se cierra y elimina la base de datos y también se puede devolver una instancia de clase de transacciones a medida con Save () y close () métodos.

public class Transaction {
    protected Transaction (SQLiteDatabase database) {
        this.database = database;
        database.beginTransaction ();
    }

    public void save () {
        this.database.setTransactionSuccessful ();
    }

    public void close () {
        this.database.endTransaction ();
    }

    private SQLiteDatabase database;
}

public Transaction createTransaction () {
    return new Transaction (this.dbHelper.getWritableDatabase ());
}

A continuación:

ContentProviderClient client = getContentResolver ().acquireContentProviderClient (Contract.authorityLocal);
Transaction tx = ((LocalContentProvider) client.getLocalContentProvider ()).createTransaction ();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top