zoukankan      html  css  js  c++  java
  • Android小技巧(二):为ContentProvider添加数据库事务支持

    转载请注明出处。博客地址:http://blog.csdn.net/mylzc

    介绍:数据库事务是由一组数据库操作序列组成,事务作为一个整体被执行。

    事务的原子性:包含在其中的对数据库的操作序列最终要么全部执行,要么全部不执行。当全部执行时,事务对数据库的修改将生效;当全部不执行时,数据库维持原有的状态,不会被修改。

    问题:最近在做一个从sdcard导入数据到数据库的功能,当导入失败时,数据库要恢复到导入前的状态。使用数据库事务处理能很好地满足到我们的需求。

    我们知道Android平台上使用的sqlite数据库是支持事务处理功能的,实现的代码如下:

    1. SQLiteDatabase db =mOpenHelper.getWritableDatabase(); 
    2.                    db.beginTransaction();//开始事务  
    3. //进行insertdelete update等数据库操作  
    4. db.setTransactionSuccessful();//设置事务标记为Successful  
    5. db.endTransaction();//提交事务 
    SQLiteDatabase db =mOpenHelper.getWritableDatabase();
                       db.beginTransaction();//开始事务
    //进行insertdelete update等数据库操作
    db.setTransactionSuccessful();//设置事务标记为Successful
    db.endTransaction();//提交事务

    可是,对于已经封装成ContentProvider的Sqlite我们应该如何让其支持事务处理功能呢?

    解决办法:查看ContentProvider的API说明文档,我们惊喜地发现applyBatch(String authority,ArrayList<ContentProviderOperation> operations)这个方法,难道只需要直接使用这个方法就可以实现事务了?

    谨慎起见我们先来看看ContentProvider的源码,最后追踪到这个方法:

    1. public ContentProviderResult[]applyBatch(ArrayList<ContentProviderOperation> operations) 
    2.             throwsOperationApplicationException { 
    3.        finalint numOperations = operations.size(); 
    4.        final ContentProviderResult[] results = newContentProviderResult[numOperations]; 
    5.        for (int i = 0; i < numOperations; i++) {//遍历数据库操作序列  
    6.             results[i] =operations.get(i).apply(this, results, i);//执行数据库操作  
    7.        } 
    8.        return results;//返回结果  
    9.    } 
    public ContentProviderResult[]applyBatch(ArrayList<ContentProviderOperation> operations)
                throwsOperationApplicationException {
           final int numOperations = operations.size();
           final ContentProviderResult[] results = newContentProviderResult[numOperations];
           for (int i = 0; i < numOperations; i++) {//遍历数据库操作序列
                results[i] =operations.get(i).apply(this, results, i);//执行数据库操作
           }
           return results;//返回结果
       }

    从上面的代码中,我们找不到和db.beginTransaction()、db.endTransaction()相似的方法,也就是说,这个方法只是进行简单的批处理,并没有保障这些数据库操作的原子性。

    好吧。我们稍微动下脑筋,覆写ContentProvider的applyBatch()方法,为其添加事务处理功能。代码如下:

    1. @Override 
    2. publicContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation>operations) 
    3.             throwsOperationApplicationException{ 
    4.           SQLiteDatabasedb = mOpenHelper.getWritableDatabase(); 
    5.           db.beginTransaction();//开始事务  
    6.           try
    7.                    ContentProviderResult[]results = super.applyBatch(operations); 
    8.                    db.setTransactionSuccessful();//设置事务标记为successful  
    9.                    returnresults; 
    10.           }finally
    11.                    db.endTransaction();//结束事务  
    12.           } 
             @Override
             publicContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation>operations)
                         throwsOperationApplicationException{
                       SQLiteDatabasedb = mOpenHelper.getWritableDatabase();
                       db.beginTransaction();//开始事务
                       try{
                                ContentProviderResult[]results = super.applyBatch(operations);
                                db.setTransactionSuccessful();//设置事务标记为successful
                                returnresults;
                       }finally {
                                db.endTransaction();//结束事务
                       }
             }

    然后,我们该如何使用这个applyBatch()方法呢?applyBatch()的第一个参数实现事务的Provider的authority属性,第二个参数是数据库操作序列,构建数据库操作的对象使用了builder设计模式,下面是一个使用applyBatch()的例子:

    1. ArrayList<ContentProviderOperation>ops = new ArrayList<ContentProviderOperation>(); 
    2. ops.add(ContentProviderOperation.newDelete(Person.CONTENT_URI).build());//添加一个删除Person表的操作  
    3. ops.add(ContentProviderOperation.newInsert(Home.CONTENT_URI).withValues(values).build());//添加一条记录到Home表  
    4. getContentResolver().applyBatch(PROVIDER.AUTHORITY,ops);//处理事务 
    ArrayList<ContentProviderOperation>ops = new ArrayList<ContentProviderOperation>();
    ops.add(ContentProviderOperation.newDelete(Person.CONTENT_URI).build());//添加一个删除Person表的操作
    ops.add(ContentProviderOperation.newInsert(Home.CONTENT_URI).withValues(values).build());//添加一条记录到Home表
    getContentResolver().applyBatch(PROVIDER.AUTHORITY,ops);//处理事务

    总结:

    1、sqlite支持事务处理操作

    2、对于封装成ContentProvider的sqlite数据库,我们可以通过覆写ContentProvider的applyBatch(Stringauthority, ArrayList<ContentProviderOperation> operations)方法来实现对事务处理的支持

  • 相关阅读:
    WPF FAQ (from Syncfusion)
    父窗口与子窗口的层次关系
    [WPF疑难]如何禁用WPF窗口的系统菜单(SystemMenu)
    【讨论】不得不重视的问题:信息太多!信息太杂!
    javaScript系列 [08]javaScript和JSON (进阶)
    javaScript系列 [11]Canvas绘图(路径)
    javaScript系列 [10]Canvas绘图(基础)
    Hexo + Github搭建个人博客
    javaScript系列 [07]日期类型和计时器
    javaScript系列 [09]javaScript和JSON (拓展)
  • 原文地址:https://www.cnblogs.com/LiaoHao/p/3270032.html
Copyright © 2011-2022 走看看