32

Mục đích: làm mới cơ sở dữ liệu từ dữ liệu XMLAndroid: giao dịch SQLite khi sử dụng ContentResolver

Quá trình:

  • Bắt đầu giao dịch
  • Xóa tất cả các hàng hiện có từ các bảng
  • Mỗi phần tử chính được phân tích cú pháp XML chèn hàng vào bảng chính và nhận được PK
  • mỗi mỗi đứa trẻ của phần tử chèn kỷ lục chính vào bảng 2 cung cấp FK từ bước trước
  • Cam kết giao dịch

thứ Khá chuẩn như xa như các hoạt động db. Vấn đề là các hoạt động CRUD không được thực hiện trong phạm vi ContentProvider mà là sử dụng ContentResolver để chèn ví dụ giống như resolver.insert(CONTENT_URI, contentValues). API ContentResolver dường như không có bất kỳ điều gì liên quan đến giao dịch và tôi không thể sử dụng bulkInsert vì tôi đang chèn vào 2 bảng không liên tục (cộng thêm tôi muốn có delete bên trong giao dịch).

Tôi đã nghĩ đến việc đăng ký tùy chỉnh ContentProvider làm người nghe bằng cách sử dụng registerContentObserver nhưng vì phương pháp ContentResolver#acquireProvider bị ẩn làm cách nào để có được tham chiếu đúng?

Tôi có gặp may không?

+2

thấy điều này: http://stackoverflow.com/questions/4655291/semantics-of-withvaluebackreference –

+0

Bạn đã bao giờ tìm thấy một giải pháp cho điều này? Tôi không thể tìm thấy một giải pháp hoạt động – jamesc

Trả lời

41

Tôi đã thấy điều đó trong mã nguồn của ứng dụng Google I/O, họ ghi đè phương thức applyBatch() và sử dụng giao dịch bên trong. Vì vậy, bạn tạo một lô ContentProviderOperation s và sau đó gọi getContentResolver().applyBatch(uri_authority, batch).

Tôi định sử dụng phương pháp này để xem cách hoạt động. Tôi tò mò nếu có ai khác thử nó.

+7

Tôi đã thử phương pháp này và nó hoạt động tốt. Tuy nhiên, mỗi ContentProviderOperation trong lô là các hoạt động nguyên tử. Điều tôi ngụ ý là không có cách nào để xử lý chính xác các hoạt động phụ thuộc cho các mối quan hệ chi tiết chủ nơi khóa nhận dạng được tạo bởi thao tác đầu tiên là cần thiết làm đầu vào cho các hoạt động tiếp theo. Tôi đã hỏi điều này trước đây, nhưng không có phản hồi (http://stackoverflow.com/questions/3224857/master-detail-using-contentresolver-applybatch). – Dan

+0

Tôi đã thử nó quá và tôi nhận thấy một hiệu suất đạt được hơn 1000 phần trăm. Chỉ cần sao chép mã từ Dự án IOShed đến Nhà cung cấp của tôi. – fmo

+0

Câu trả lời này không chính xác. Hàm mặc định của 'applyBatch()' chỉ lặp lại các thao tác và áp dụng chúng một cách riêng biệt. Điều này chỉ cung cấp một phương tiện để thực hiện một giao dịch, bằng cách ghi đè 'applyBatch()' trong 'ContentProvider' của bạn. Nó không cung cấp hành vi giao dịch trên chính nó. Nếu bạn không kiểm soát việc thực thi 'ContentProvider', bạn sẽ không gặp may. –

4

Tất cả các quyền - vì vậy điều này không dingle aimlessly: cách duy nhất tôi có thể nghĩ là mã startTransaction và endTransaction như yêu cầu truy vấn dựa trên URL. Một cái gì đó như ContentResolver.query(START_TRANSACTION, null, null, null, null). Sau đó, trong ContentProvider#query dựa trên giao dịch bắt đầu hoặc kết thúc cuộc gọi URL đã đăng ký

16

Có thể thực hiện giao dịch dựa trên nhiều bảng chèn khá sạch kể từ Android 2.1 bằng cách sử dụng ContentProviderOperation, như đã đề cập bởi kaciula.

Khi bạn xây dựng đối tượng ContentProviderOperation, bạn có thể gọi .withValueBackReference (fieldName, refNr). Khi hoạt động được áp dụng bằng hàm applyBatch, kết quả là đối tượng ContentValues ​​được cung cấp với lệnh insert() sẽ có một số nguyên được tiêm. Số nguyên sẽ được keyed với fieldName String, và giá trị của nó được lấy từ ContentProviderResult của một ContentProviderOperation được áp dụng trước đó, được lập chỉ mục bởi refNr.

Vui lòng tham khảo mẫu mã bên dưới. Trong mẫu, một hàng được chèn vào bảng 1 và ID kết quả (trong trường hợp này là "1") sau đó được sử dụng như một giá trị khi chèn hàng trong bảng 2. Để ngắn gọn, trình ContentProvider không được kết nối với cơ sở dữ liệu. Trong ContentProvider, có các bản in nơi nó sẽ phù hợp để thêm xử lý giao dịch.

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

Bạn có thể nhận việc thực hiện các đối tượng cung cấp nội dung bản thân (nếu trong quá trình đó, gợi ý: bạn có thể kiểm soát quá trình của nhà cung cấp với đa tiến = "true" hoặc quá trình = "" http://developer.android.com/guide/topics/manifest/provider-element.html) sử dụng ContentProviderClient.getLocalContentProvider() có thể được đưa vào triển khai nhà cung cấp của bạn, có thể cung cấp thêm chức năng như thiết lập lại() đóng và xóa cơ sở dữ liệu và bạn cũng có thể trả về cá thể lớp Giao dịch tùy chỉnh bằng phương thức save() và close().

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

Sau đó:

ContentProviderClient client = getContentResolver().acquireContentProviderClient (Contract.authorityLocal); 
Transaction tx = ((LocalContentProvider) client.getLocalContentProvider()).createTransaction(); 
Các vấn đề liên quan