32

的目標時,SQLite的交易:從XML數據刷新數據庫安卓:使用ContentResolver的

的過程:

  • 開始交易
  • 從表中刪除所有現有行
  • 每解析XML的每個主要元素行插入主表並獲取PK
  • %的主要元素插入記錄的每個孩子推到第二臺從上一步驟
  • 提供FK提交交易

漂亮的標準的東西,只要數據庫操作。問題在於,在ContentProvider內沒有完成CRUD操作,而是使用了ContentResolver,因此插入例如看起來像resolver.insert(CONTENT_URI, contentValues)。 ContentResolver API似乎沒有任何與事務相關的內容,我不能使用bulkInsert,因爲我間歇性地插入了2個表格(此外我還希望在事務中有delete)。

我正在考慮使用registerContentObserver註冊我自定義的ContentProvider作爲聽衆,但由於ContentResolver#acquireProvider方法被隱藏,因此我如何獲得正確的參考?

我運氣不好嗎?

+2

看到這一點:http://stackoverflow.com/questions/4655291/semantics-of-withvaluebackreference – 2011-10-25 15:31:12

+0

你有沒有找到一個解決這個?我找不到可行的解決方案 – jamesc 2011-11-11 21:10:01

回答

41

我已經看到了,在谷歌I/O應用程序的源代碼,它們覆蓋在它的內部ContentProviderapplyBatch()方法和用途的交易。因此,您創建了一批ContentProviderOperation s,然後致電getContentResolver().applyBatch(uri_authority, batch)

我打算使用這種方法來看看它是如何工作的。我很好奇,如果有人嘗試過。

+7

我試過這種方法,效果很好。但批處理中的每個ContentProviderOperation都是原子操作。我的意思是,沒有辦法正確處理主從關係的依賴操作,其中第一個操作創建的標識關鍵字需要作爲後續操作的輸入。我之前已經問過這個,但得到零響應(http://stackoverflow.com/questions/3224857/master-detail-using-contentresolver-applybatch)。 – Dan 2010-10-15 18:42:58

+0

我也試過了,我注意到性能增益超過1000%。只需將IOShed項目中的代碼複製到我的提供程序即可。 – fmo 2012-05-31 23:24:13

+0

這個答案不正確。 'applyBatch()'的默認impl只會遍歷這些操作並將它們單獨應用。這隻通過在你的ContentProvider中重寫'applyBatch()'來提供一種實現事務的方法。它本身並不提供交易行爲。如果你不控制'ContentProvider'實現,那麼你運氣不好。 – 2017-02-21 21:47:37

4

好的 - 所以這不會漫不經心:我能想到的唯一方法是將startTransaction和endTransaction編碼爲基於URL的查詢請求。類似於ContentResolver.query(START_TRANSACTION, null, null, null, null)。然後在ContentProvider#query基於註冊的網址通話的開始或結束交易

16

正如kaciula提到的那樣,通過使用ContentProviderOperation可以很方便地完成基於事務的多表插入。

當您構建ContentProviderOperation對象時,可以調用.withValueBackReference(fieldName,refNr)。當使用applyBatch應用操作時,結果是由insert()調用提供的ContentValues對象將注入一個整數。該整數將用fieldName字符串鍵入,並且其值從以前應用的ContentProviderOperation的ContentProviderResult中檢索,由refNr索引。

請參考下面的代碼示例。在示例中,將一行插入到table1中,然後將結果ID(本例中爲「1」)用作在表2中插入行時的值。爲簡潔起見,ContentProvider未連接到數據庫。在ContentProvider中,有打印輸出適合添加事務處理。使用ContentProviderClient.getLocalContentProvider:

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

你可以得到內容提供商對象本身的執行(你可以控制供應商與多進程=「true」或過程=「」 http://developer.android.com/guide/topics/manifest/provider-element.html過程中,如果在同一個進程,暗示) ()可以被轉換爲你的提供者實現,它可以提供額外的功能,比如關閉和刪除數據庫的reset(),你也可以用save()和close()方法返回一個自定義的Transaction類實例。

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

然後:

ContentProviderClient client = getContentResolver().acquireContentProviderClient (Contract.authorityLocal); 
Transaction tx = ((LocalContentProvider) client.getLocalContentProvider()).createTransaction();