原文链接:http://www.orlion.ga/610/

一、事务

SQLite支持事务,看一下Android如何使用事务:比如 Book表中的数据都已经很老了,现在准备全部废弃掉替换成新数据,可以先使用delete()方法将Book表中的数据删除, 然后再使用insert()方法将新的数据添加到表中。我们要保证的是,删除旧数据和添加新数据的操作必须一起完成,否则就还要继续保留原来的旧数据。

                Button replaceData = (Button) findViewById(R.id.replace_data);
replaceData.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction();
db.delete("book", null, null);
try {
if (true) {
// 手动抛出异常,让事务失败
throw new Exception();
} ContentValues values = new ContentValues();
values.put("name", "book new");
values.put("author", "orlion");
values.put("pages", 200);
values.put("price", 100); db.insert("book", null, values);
db.setTransactionSuccessful(); // 事务已经执行成功
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction(); // 结束事务
}
}
});

上述代码就是Android中事务的标准用法, 首先调用SQLiteDatabase的beginTransaction()方法来开启一个事务,然后在一个异常捕获的代码块中去执行具体的数据库操作,当所有的操作都完成之后,调用 setTransactionSuccessful()表示事务已经执行成功了,最后在 finally代码块中调用 endTransaction()来结束事务。注意观察,我们在删除旧数据的操作完成后手动抛出了一个 NullPointerException,这样添加新数据的代码就执行不到了。不过由于事务的存在,中途出现异常会导致事务的失败,此时旧数据应该是删除不掉的。

二、升级数据库的最佳写法

// 这里直接复制《第一行代码原文》

Android入门(十)SQLite创建升级数据库 一文中升级数据库的方式是非常粗暴的,为了保证数据库中的表是最新的,我们只是简单地在 onUpgrade()方法中删除掉了当前所有的表,然后强制重新执行了一遍 onCreate()方法。这种方式在产品的开发阶段确实可以用,但是当产品真正上线了之后就绝对不行了。

每一个数据库版本都会对应一个版本号, 当指定的数据库版本号大于当前数据库版本号的时候, 就会进入到 onUpgrade()

方法中去执行更新操作。这里需要为每一个版本号赋予它各自改变的内容,然后在onUpgrade()方法中对当前数据库的版本号进行判断,再执行相应的改变就可以了。

接着就让我们来模拟一个数据库升级的案例,还是由 MyDatabaseHelper类来对数据库进行管理。第一版的程序要求非常简单,只需要创建一张 Book表,MyDatabaseHelper中的代码如下所示:

public class MyDatabaseHelper extends SQLiteOpenHelper {
    public static final String CREATE_BOOK = "create table Book ("
        + "id integer primary key autoincrement, "
        + "author text, "
        + "price real, "
        + "pages integer, "
        + "name text)";
    public MyDatabaseHelper(Context context, String name, CursorFactory
        factory, int version) {
        super(context, name, factory, version);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

不过,几星期之后又有了新需求,这次需要向数据库中再添加一张 Category表。于是,修改 MyDatabaseHelper中的代码,如下所示:

public class MyDatabaseHelper extends SQLiteOpenHelper {
    public static final String CREATE_BOOK = "create table Book ("
        + "id integer primary key autoincrement, "
        + "author text, "
        + "price real, "
        + "pages integer, "
        + "name text)";
    public static final String CREATE_CATEGORY = "create table Category ("
        + "id integer primary key autoincrement, "
        + "category_name text, "
        + "category_code integer)";
    public MyDatabaseHelper(Context context, String name,
        CursorFactory factory, int version) {
        super(context, name, factory, version);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {
        case 1:
            db.execSQL(CREATE_CATEGORY);
            default:
        }
    }
}

可以看到,在 onCreate()方法里我们新增了一条建表语句,然后又在 onUpgrade()方法中添加了一个 switch判断,如果用户当前数据库的版本号是 1,就只会创建一张 Category表。这样当用户是直接安装的第二版的程序时,就会将两张表一起创建。而当用户是使用第二版的程序覆盖安装第一版的程序时,就会进入到升级数据库的操作中,此时由于 Book表已经存在了,因此只需要创建一张 Category表即可。但是没过多久,新的需求又来了,这次要给 Book表和 Category表之间建立关联,需要在 Book表中添加一个 category_id的字段。 再次修改 MyDatabaseHelper中的代码, 如下所示:

public class MyDatabaseHelper extends SQLiteOpenHelper {
    public static final String CREATE_BOOK = "create table Book ("
        + "id integer primary key autoincrement, "
        + "author text, "
        + "price real, "
        + "pages integer, "
        + "name text, "
        + "category_id integer)";
    public static final String CREATE_CATEGORY = "create table Category ("
        + "id integer primary key autoincrement, "
        + "category_name text, "
        + "category_code integer)";
    public MyDatabaseHelper(Context context, String name,
        CursorFactory factory, int version) {
        super(context, name, factory, version);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {
        case 1:
            db.execSQL(CREATE_CATEGORY);
        case 2:
            db.execSQL("alter table Book add column category_id integer");
        default:
        }
    }
}

可以看到,首先我们在 Book表的建表语句中添加了一个 category_id列,这样当用户直接安装第三版的程序时,这个新增的列就已经自动添加成功了。然而,如果用户之前已经安装了某一版本的程序,现在需要覆盖安装,就会进入到升级数据库的操作中。在 onUpgrade()方法里,我们添加了一个新的 case,如果当前数据库的版本号是 2,就会执行 alter命令来为Book表新增一个 category_id列。

这里请注意一个非常重要的细节,switch中每一个 case的最后都是没有使用 break的,为什么要这么做呢?这是为了保证在跨版本升级的时候, 每一次的数据库修改都能被全部执行到。比如用户当前是从第二版程序升级到第三版程序的,那么 case 2中的逻辑就会执行。而如果用户是直接从第一版程序升级到第三版程序的,那么 case 1和 case 2中的逻辑都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失了。

Android入门(十二)SQLite事务、升级数据库的更多相关文章

  1. android 入门 006(sqlite增删改查)

    android 入门 006(sqlite增删改查) package cn.rfvip.feb_14_2_sqlite; import android.content.Context; import ...

  2. Android Studio(十二):打包多个发布渠道的apk文件

    Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...

  3. Android入门(十)SQLite创建升级数据库

    原文链接:http://www.orlion.ga/603/ 一.创建数据库 Android为了让我们能够更加方便地管理数据库,专门提供了一个 SQLiteOpenHelper帮助类, 借助这个类就可 ...

  4. JavaWeb学习总结(十二)--事务

    一.事务的介绍 1.1 什么是事务 银行转账!张三转10000块到李四的账户,这其实需要两条SQL语句: 给张三的账户减去10000元: 给李四的账户加上10000元. 如果在第一条SQL语句执行成功 ...

  5. Android入门(十六)调用摄像头相册

    原文链接:http://www.orlion.ga/665/ 一.调用摄像头 创建一个项目ChoosePicDemo,修改activity_main.xml: <LinearLayout xml ...

  6. Android入门(十四)内容提供器-实现跨程序共享实例

    原文链接:http://www.orlion.ga/661/ 打开SQLite博文中创建的 DatabaseDemo项目,首先将 MyDatabaseHelper中使用 Toast弹出创建数据库成功的 ...

  7. android学习十二(android的Content Provider(内容提供器)的使用)

    文件存储和SharePreference存储以及数据存储一般为了安全,最好用于当前应用程序中訪问和存储数据.内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能 ...

  8. Android入门(二):Android的系统架构

    android的系统架构和其操作系统一样,采用了分层的架构.从架构图看,android分为四个层,从高层到低层分别是应用程序层.应用程序框架层.系统运行库层和linux核心层.   从技术方面看,An ...

  9. Java开发学习(二十二)----Spring事务属性、事务传播行为

    一.事务配置 上面这些属性都可以在@Transactional注解的参数上进行设置. readOnly:true只读事务,false读写事务,增删改要设为false,查询设为true. timeout ...

随机推荐

  1. 测试文档锁:doc.LockDocument()

    /// <summary> /// 总结:用到DocumentManager.Open(filePath)时,如果是ForWrite,就需要用到lock文档锁. /// </summ ...

  2. Django 中 如何使用 settings.py 中的常量

    在用django 框架开发 python web 程序的时候 , 在模板页面经常会用到 settings.py 中设置的常量,比如MEDIA_URL, 我尝试过在模板页面用类似如下的方式 程序代码 { ...

  3. Mac OS环境下配置Myeclipse2015的经验

    反复测试装了多次,现在把成功安装的方法陈列如下: 1. 相关的资源: (1)下载 myeclipse-2015-stable-2.0-offline-installer-macosx.dmg 链接:h ...

  4. 如何在MFC中创建非矩形button

    一般情况下,我们创建的按钮都是矩形的,但有时为了满足特殊的需求,我们要在对话框中创建一个非矩形的按钮,比如,圆形,椭圆等. 要实现一个非矩形的按钮,这就涉及到了自绘控件.自绘控件的方法有很多,可以参考 ...

  5. sphinx的配置

    ## Sphinx configuration file sample## WARNING! While this sample file mentions all available options ...

  6. 【Win10】UAP/UWP/通用 开发之 RelativePanel

    [Some information relates to pre-released product which may be substantially modified before it's co ...

  7. 我的ORM之九 -- 生成器

    我的ORM索引 数据库连接字符串格式 <add name="dbo" connectionString="" providerName="MyS ...

  8. 在MVVM模式中,按钮Click事件的绑定方法

    在MVVM模式中,我们将Button的方法写到ViewModel中,然后绑定到前端界面.通常的做法是写一个类,继承ICommand接口,然而如果按钮比较多的话,就需要写很多的类,对于后期维护造成很大的 ...

  9. Android Fragment add/replace以及backstack

    无论Fragment以何种方式加入,都不会影响backstack,backstack由addToBackStack函数决定,只有调用了这个函数,才会将Fragment加入返回栈.这个说法其实不太准确, ...

  10. 你不可不知的HTML优化技巧

    如何提升Web页面的性能,很多开发人员从多个方面来下手如JavaScript.图像优化.服务器配置,文件压缩或是调整CSS. 很显然HTML 已经达到了一个瓶颈,尽管它是开发Web 界面必备的核心语言 ...