Multiple rows insert with ContentProvider

后端 未结 4 1968
醉话见心
醉话见心 2021-02-02 00:23

I need to make insert of few rows in one transaction. Can I do it with ContentProvider?

相关标签:
4条回答
  • 2021-02-02 00:28

    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
     */
    @Override
    public 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
    // received
    noteIdIndex = 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.

    0 讨论(0)
  • 2021-02-02 00:33

    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.

    0 讨论(0)
  • 2021-02-02 00:37

    I also use replace mode for insert row - db.insertWithOnConflict(EVENT_TABLE_NAME, null, value, SQLiteDatabase.CONFLICT_REPLACE); Its will rid of conflict if record is exist already

    In DatabaseHelper add UNIQUE INDEX

        public class DataProvider extends ContentProvider {
    
        private static class DatabaseHelper extends SQLiteOpenHelper {
            DatabaseHelper(Context context){
                super(context, DATABASE_NAME, null, DATABASE_VERSION);
            }
    
            @Override
            public void onCreate(SQLiteDatabase db){
                db.execSQL(CREATE_EVENT_TABLE);
                db.execSQL("CREATE UNIQUE INDEX event_idx ON " + EVENT_TABLE_NAME + " ( " + EventTable.EVENT_ID + " )");
    // ...
    
            ...
            @Override
            public int bulkInsert(Uri uri, ContentValues[] values) {
                Log.i(TAG, "bulkInsert");
                if (values.length == 0)
                    return 0;
                int insertCount = 0;
                try {
                    switch (uriMatcher.match(uri)) {
                        case EVENT_LIST:
                            try {
                                db.beginTransaction();
                                for (ContentValues value : values) {
                                    long id = db.insertWithOnConflict(EVENT_TABLE_NAME, null, value, SQLiteDatabase.CONFLICT_REPLACE);
                                    if (id > 0)
                                        insertCount++;
                                }
                                db.setTransactionSuccessful();
                            } catch (Exception e) {
                                // Your error handling
                            } finally {
                                db.endTransaction();
                            }
                            break;
                        default:
                            throw new IllegalArgumentException("Unknown URI " + uri);
                    }
                    getContext().getContentResolver().notifyChange(uri, null);
                } catch (Exception e) {
                    Log.i(TAG, "Exception : " + e);
                }
                return insertCount;
            }
    

    And call bulkInsert like this:

                ContentValues[] cvArr = new ContentValues[eventList.size()];
                long insertCount = 0;
                int i = 0;
                for (Event event : eventList) {
                    ContentValues cv = new ContentValues();
                    cv.put(DataProvider.EventTable.EVENT_ID, event.id);
                    cv.put(DataProvider.EventTable.SENSOR_ID, event.sensor_id);
                    cv.put(DataProvider.EventTable.TIMESTAMP, event.time);
                    cvArr[i++] = cv;
                }
                // returns ID
                insertCount = context.getContentResolver()
                        .bulkInsert(DataProvider.CONTENT_EVENT_LIST, cvArr);
    
    0 讨论(0)
  • 2021-02-02 00:54

    Here an example for bulkInsert:

    /**
     * Perform bulkInsert with use of transaction
     */
    @Override
    public 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;
    }
    
    0 讨论(0)
提交回复
热议问题