Android入门(十二)SQLite事务、升级数据库

原文链接: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中的逻辑都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失了。

时间: 2024-10-24 18:08:08

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

Android技术8:SQLite数据升级

由于应用程序的升级,往往伴随着数据库的升级,数据库升级一般设计表的增加与删除,表添加删除字段,数据的备份等操作. 1.数据库升级 我们都通过继承SQLiteOpenHelper类,实现对数据库的操作和版本升级等.版本升级有关方法 onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion); 当版本号大于当前数据库版本号,会调用onUpgrade方法. 2.演示数据库升级 对于表的增删相对比较简单,下面演示添加字段. 2.1原始数据库s

[WebGL入门]十二,模型数据和顶点属性

注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正. 顶点属性的意思 上次的文章中,介绍了一下从着色器的生成,编译,到程序对象的生成和着色器的连接.这次,简单的说一下模型数据的定义和顶点属性的处理.另外,介绍一下根据模型数据生成VBO的方法.VBO的使用要比生成难理解一些,但是不要担心,后面会慢慢说明.接下来看一下顶点属性.顶点属性,说的简单点,

Android之十二微信UI界面设计

Android之十二微信UI界面设计 corners_bg.xml <span style="font-size:14px;"><?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color=&qu

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

原文链接:http://www.orlion.ga/603/ 一.创建数据库 Android为了让我们能够更加方便地管理数据库,专门提供了一个 SQLiteOpenHelper帮助类, 借助这个类就可以非常简单地对数据库进行创建和升级. SQLiteOpenHelper是一个抽象类,其中有两个抽象方法分别是onCreate()和onUpgrade(),分别在这两个方法中实现创建和升级数据库的逻辑 SQLiteOpenHelper还有两个十分重要的实例方法,getReadableDatabase(

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

原文链接:http://www.orlion.ga/665/ 一.调用摄像头 创建一个项目ChoosePicDemo,修改activity_main.xml: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent&qu

Android数据存储之Sqlite采用SQLCipher数据库加密实战

前言: 最近研究了Android Sqlite数据库(文章地址:http://www.cnblogs.com/whoislcj/p/5506294.html)以及ContentProvider程序间数据共享(http://www.cnblogs.com/whoislcj/p/5507928.html),我们清晰的知道Sqlite数据库默认存放位置data/data/pakage/database目录下,对于已经ROOT的手机来说的没有任何安全性可以,一旦被利用将会导致数据库数据的泄漏,所以我们该

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

文件存储和SharePreference存储以及数据存储一般为了安全,最好用于当前应用程序中访问和存储数据.内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性.目前使用内容提供器是android实现跨程序共享数据的标准方式.内容提供器可以选择只对一部分数据进行共享,从而保证我们的程序中的隐私数据不会有泄漏的风险. 内容提供器的用法一般有两种,一种是使用现有的内容提

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

原文链接:http://www.orlion.ga/661/ 打开SQLite博文中创建的 DatabaseDemo项目,首先将 MyDatabaseHelper中使用 Toast弹出创建数据库成功的提示去除掉,因为跨程序访问时我们不能直接使用 Toast.然后添加一个 DatabaseProvider类,代码如下所示: package ga.orlion.databasedemo; import android.content.ContentProvider; import android.c

Android 学习心得(3)——SQLite事务

应用程序初始化时需要批量的向sqlite中插入大量数据,单独的使用for+Insert方法导致应用响应缓慢,因为 sqlite插入数据的时候默认一条语句就是一个事务,有多少条数据就有多少次磁盘操作.我的应用初始5000条记录也就是要5000次读写磁盘操作. 而且不能保证所有数据都能同时插入.(有可能部分插入成功,另外一部分失败,后续还得删除.太麻烦) 解决方法: 添加事务处理,把5000条插入作为一个事务 我们使用SQLite的事务进行控制: 1 db.beginTransaction(); /