2011-07-27 13 views
12

1回のトランザクションで少数の行を挿入する必要があります。 ContentProviderでこれを行うことはできますか?ContentProviderを使用した複数行の挿入

+0

質問は不明..ですとにかくあなたがここのhttp見ることができます://developer.android.com/guide/topics/providers/content-providers.html#modifying –

+0

http://stackoverflow.com/questions/5596354/insertion-of-thousands-of-contact-entries-using-applybatch -is-slow – Selvin

答えて

18

クライアント側では、ContentResolverbulkInsert()メソッドをサポートしています。 ContentProviderによって実行される取引がない可能性があるため、これらは必ずContentProviderによって1回の取引で処理されるとは限りません。

+1

私が理解しているように、 verrriden - それはちょうどstandrt '挿入'メソッドを呼び出しませんか? – earsonheart

+2

正しい。与えられた 'ContentProvider'があなた自身の' ContentProvider'でない限り、 'bulkInsert()'をオーバーライドするかどうか分かりません。 – CommonsWare

+0

私はContentProviderOperationsと一緒に行くべきだと思っています。このチュートリアルhttp://www.grokkingandroid.com/better-performance-with-contentprovideroperation/ –

21

私はこれを私のアプリで実装していますが、ここでは私が使用するコードの要点です。私のコンテンツプロバイダで

、私はapplyBatch()メソッドをオーバーライドして、それは上書きすることは非常に簡単な方法です:あなたが参照をバックサポートしたいので

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

結果は、次の動作に与えられています。私は実際に私のコンテンツに対するこの単一のトランザクションIループ内のデータベースで何かを変更し、このようなものをしたいときは:

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()); 

あなただけ呼び出すことによって終了する覚えておく必要があります。

provider.applyBatch(operations); 

この以前の挿入物からのIDが問題なく必要な場合は、単一のトランザクションであなたのものを実行し、逆参照をサポートします。

6

ここ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; 
} 

そして、あなたのコードのようなもの:

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

を元のデータ配列からループ内の正規の挿入物を呼び出すよりもどのように優れていますか? BulkInsertを使用するとパフォーマンス上の利点はありますか? –

+2

@AndrewS bulkInsert()は大きな操作ではるかに優れています。私のアプリでちょうど完成した最適化:applyBatch()はいくつかのテーブルで2000回の操作で2000ミリ秒かかるが、10バルクインサートは100ミリ秒かかる。 –

1

私はまた、挿入行のモードを置き換える使用 - ヌル db.insertWithOnConflict(EVENT_TABLE_NAME、 、値、SQLiteDatabase.CONFLICT_REPLACE);レコードは、このようbulkInsertを

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; 
     } 

をUNIQUE INDEXを追加し、呼すでにDatabaseHelperで

存在している場合 その競合を排除します:

  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); 
関連する問題