Multiple rows insert with ContentProvider
I have implemented this in my app and here's the gist of the code that I use.
In my content provider, I have overridden the applyBatch() method and it's a very simple method to override:
/** * Performs the work provided in a single transaction */@Overridepublic ContentProviderResult[] applyBatch( ArrayList<ContentProviderOperation> operations) { ContentProviderResult[] result = new ContentProviderResult[operations .size()]; int i = 0; // Opens the database object in "write" mode. SQLiteDatabase db = mOpenHelper.getWritableDatabase(); // Begin a transaction db.beginTransaction(); try { for (ContentProviderOperation operation : operations) { // Chain the result for back references result[i++] = operation.apply(this, result, i); } db.setTransactionSuccessful(); } catch (OperationApplicationException e) { Log.d(TAG, "batch failed: " + e.getLocalizedMessage()); } finally { db.endTransaction(); } return result;}
The result is given to the next operation because you want to support back references. When I actually want to change stuff in the database in this single transaction I loop over my content and do stuff like this:
operations.add(ContentProviderOperation .newInsert( Uri.withAppendedPath( NotePad.Notes.CONTENT_ID_URI_BASE, Long.toString(task.dbId))) .withValues(task.toNotesContentValues(0, listDbId)) .build());// Now the other table, use back reference to the id the note// receivednoteIdIndex = operations.size() - 1;operations.add(ContentProviderOperation .newInsert(NotePad.GTasks.CONTENT_URI) .withValues(task.toGTasksContentValues(accountName)) .withValueBackReferences( task.toGTasksBackRefContentValues(noteIdIndex)) .build());
You just need to remember to finish by calling:
provider.applyBatch(operations);
This will perform your stuff in a single transaction and supports backreferences if you need the id from an earlier insert without issue.
On the client side, ContentResolver
supports a bulkInsert()
method. Those will not necessarily be processed in a single transaction by the ContentProvider
, simply because there may not be any transactions performed by the ContentProvider
.
Here an example for bulkInsert:
/** * Perform bulkInsert with use of transaction */@Overridepublic int bulkInsert(Uri uri, ContentValues[] values) { int uriType = 0; int insertCount = 0; try { uriType = sURIMatcher.match(uri); SQLiteDatabase sqlDB = dbHelper.getWritableDatabase(); switch (uriType) { case MEASUREMENTS: try { sqlDB.beginTransaction(); for (ContentValues value : values) { long id = sqlDB.insert(Tab_Measurements.TABLE_NAME, null, value); if (id > 0) insertCount++; } sqlDB.setTransactionSuccessful(); } catch (Exception e) { // Your error handling } finally { sqlDB.endTransaction(); } break; default: throw new IllegalArgumentException("Unknown URI: " + uri); } // getContext().getContentResolver().notifyChange(uri, null); } catch (Exception e) { // Your error handling } return insertCount;}
And in your code something like:
/** * Inserts new measurement information. * * @param ArrayList of measurements * @return number of inserted entries */public static long bulkInsertEntries(ArrayList<Item_Measurement> readings) { // insert only if data is set correctly if (readings.size() == 0) return 0; long insertCount = 0; try { // insert new entries // ArrayList<ContentValues> valueList = new ArrayList<ContentValues>(); ContentValues[] valueList = new ContentValues[readings.size()]; int i = 0; for (Item_Measurement reading : readings) { ContentValues values = new ContentValues(); values.put(COL_TIME_READING, reading.getTimeReading()); // ... valueList[i++] = values; } // returns ID insertCount = ContentProviderOwn.getAppContext().getContentResolver() .bulkInsert(ContentProviderOwn.MEASUREMENTS_URI_BASE, valueList); } catch (Exception e) { // Your error handling } return insertCount;}