代码之家  ›  专栏  ›  技术社区  ›  Bostone

android:使用ContentResolver时的sqlite事务

  •  32
  • Bostone  · 技术社区  · 14 年前

    目标:从XML数据刷新数据库

    过程:

    • 启动事务
    • 删除 表中的所有现有行
    • 每解析的XML的每个主要元素 插入 进入主表并获取pk
    • 每个主元素的子元素 插入 记录到第二个表中,提供上一步的FK
    • 提交事务

    对于数据库操作来说是相当标准的。问题是CRUD操作不是在 ContentProvider 而是使用 ContentResolver 例如,插入看起来像 resolver.insert(CONTENT_URI, contentValues) . ContentResolver API似乎没有任何与事务相关的内容,因此我无法使用 bulkInsert 因为我间歇地插入两个表(另外我想 delete 也在事务内部)。

    我想注册我的定制 内容提供者 通过使用 registerContentObserver 但自从 ContentResolver#acquireProvider 方法是隐藏的,如何获取正确的引用?

    我运气不好吗?

    4 回复  |  直到 11 年前
        1
  •  41
  •   Catalin Morosan    14 年前

    我已经看到在谷歌I/O应用程序的源代码中,它们重写了 ContentProvider applyBatch() 方法并使用其中的事务。因此,您可以创建一批 ContentProviderOperation 然后调用 getContentResolver().applyBatch(uri_authority, batch) .

    我打算用这种方法来看看它是如何工作的。我很好奇是否还有人尝试过。

        2
  •  16
  •   David Burström    13 年前

    正如Kaciula所提到的,由于android 2.1使用ContentProviderOperation,因此可以非常干净地执行基于事务的多表插入。

    当您构建ContentProviderOperation对象时,可以调用.WithValueBackReference(FieldName,Refnr)。当使用applybatch应用该操作时,结果是随insert()调用提供的contentValues对象将注入一个整数。该整数将使用字段名字符串进行键控,其值将从以前应用的ContentProvider属性的ContentProviderResult中检索,该属性由refnr索引。

    请参阅下面的代码示例。在示例中,在表1中插入一行,然后在表2中插入行时,将生成的ID(在本例中为“1”)用作值。为了简洁起见,ContentProvider未连接到数据库。在ContentProvider中,有一些打印输出适合添加事务处理。

    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
    }
    
        3
  •  4
  •   Bostone    14 年前

    好吧-所以这并不是漫无目的的:我唯一能想到的方法是将startTransaction和endTransaction编码为基于URL的查询请求。有点像 ContentResolver.query(START_TRANSACTION, null, null, null, null) .然后在 ContentProvider#query 基于注册的URL调用开始或结束事务

        4
  •  0
  •   Eric Woodruff    11 年前

    您可以获取内容提供者对象本身的实现(如果在同一进程中,提示:您可以使用multiprocess=“true”或process=“”控制提供者的进程) http://developer.android.com/guide/topics/manifest/provider-element.html )使用ContentProviderClient.GetLocalContentProvider(),它可以强制转换到提供程序实现中,提供额外的功能,如关闭和删除数据库的reset(),还可以使用save()和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 ());
    }
    

    然后:

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