End

数据库 简介 升级 SQLite 总结

本文地址


目录

SQLite 数据库升级

两个核心方法

当我们创建 SQLiteOpenHelper 对象的时候,如果传入的版本号大于之前的版本号,onUpgrade 方法就会被调用,通过判断 oldVersion 和 newVersion 就可以决定如何升级数据库。升级完成后,数据库会自动存储最新的版本号为当前数据库版本号。

public class DatabaseHelper extends SQLiteOpenHelper {

    public DatabaseHelper(Context context, String name, CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    //在第一次打开数据库的时候才会走,清除数据后再次运行也会走
    //数据库升级的时候不会走
    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    //只有当数据库已经存在,而且版本升高的时候,才会走(即:数据库升级的时候才会走)
    //第一次创建数据库的时候不会走,清除数据后再次运行也不会走
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

基本思想

当数据库多次升级后,你就无法保证用户是从哪个数据库版本升上来的,此时处理的方法一般为:使用不带break的switch语句迭代升级。

  • 优点:每次更新数据库的时候只需要在 onUpgrade 方法的末尾加一段从上个版本升级到新版本的代码,易于理解和维护
  • 缺点:当版本变多之后,多次迭代升级可能比较费时

另一种方式是,使用if判断,针对当前 newVersion 版本号,对每个版本的数据库进行升级操作

  • 优点:跨版本升级效率高,可以保证每个版本的用户都可以在消耗最少的时间升级到最新的数据库而无需做无用的数据多次转存
  • 缺点:代码维护量将越来越大,强迫开发者记忆所有版本数据库的完整结构,且每次升级时 onUpgrade 方法都必须全部重写
  • 结论:这种跳跃升级不建议使用

ALTER 命令的局限性

sqlite 官方说明

SQLite supports a limited subset of ALTER TABLE. The ALTER TABLE command in SQLite allows the user to rename a table or to add a new column to an existing table. It is not possible to rename a column, remove a column, or add or remove constraints from a table.

SQLite 支持 ALTER TABLE 的有限子集。SQLite 中的 ALTER TABLE 命令允许用户重命名表或向现有表添加新列(并且只能在表的末尾添加新列)。无法重命名列,删除列,或从表中添加或删除约束。

如果真的需要重命名列或删除列,只能通过下面的方式间接实现:

  • 将表名改成临时表:ALTER TABLE Order RENAME TO _Order;
  • 创建新表:CREATE TABLE Order(...);
  • 导入数据:INSERTINTO Order SELECT id, “”, Age FROM _Order;
  • 删除临时表:DROP TABLE _Order;

数据库迁移

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    switch (oldVersion) {
    //注意,这里的 switch 语句是没有 break 的,会一直执行到语句结束
    case 1:
        db.execSQL(CREATE_TBL_CATEGORY)  // 建立新表
    case 2:
        db.execSQL("ALTER TABLE Book ADD COLUMN category_id INTEGER");  // 增加新列(字段)
        //注意每次的 case 中添加的创建新表的sql代码不要忘了在 onCreate 中同时添加,因为新用户也是要执行的
    case 3:
        //删除列,或者更改一个已经存在的字段的名称、数据类型、限定符等,需要建立新表,并将原表的数据复制到新表中
        db.beginTransaction();
        db.execSQL("alter table book rename to _temp_book"); //旧的表先修改为一个临时名
        db.execSQL("create table book(bookId integer primarykey, bookName text);"); //重新创建一个新的表
        db.execSQL("insert into book select *,'' from _temp_book"); //同步旧的数据到新的表
        db.execSQL("drop table _temp_book"); //删除旧的表
        db.setTransactionSuccessful();
        db.endTransaction();
    default:
        break;
    }
}

案例

参考

第一版需求

有个图书管理相关的 App,有一张图书表 —— Book,其表结构如下

字段名 类型 说明
id INTEGER 主键
name TEXT 书名
author TEXT 作者
price INTEGER 价格
pages INTEGER 页数
public class BookStoreDbHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "BookStore.db";
    private static final int VERSION = 1;

    private static final String CREATE_TBL_BOOK = "CREATE TABLE Book ("
        + "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        + "name TEXT, "
        + "author TEXT, "
        + "prices INTEGER, "
        + "pages INTEGER"
        + ")";

    public BookStoreDbHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TBL_BOOK); //创建表1
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

第二版需求

几个星期之后,第二版的需求来了,需要增加一个书籍分类表 —— Category,其表结构如下:

字段名 类型 说明
id INTEGER 主键
category_name TEXT 分类名称
category_code INTEGER 分类代码

这个时候,就需要对数据库进行升级,增加一张表。

private static final int VERSION = 2;  // 版本号加 1

// Categroy 建表语句
private static final String CREATE_TBL_CATEGORY = "CREATE TABLE Category ("
    + "id INTEGER PRIMARY KEY AUTOINCREMENT, "
    + "category_name TEXT, "
    + "category_code INTEGER"
    + ")";

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL(CREATE_TBL_BOOK);  // 建立表1
    db.execSQL(CREATE_TBL_CATEGORY)  // 建立表2
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    switch (oldVersion) {
        case 1:
            db.execSQL(CREATE_TBL_CATEGORY)  // 建立表2
        default:
            break;
    }
}

注意到一点,这是第二版的 App,那么就会有两种用户:

  • 之前没有安装过这款 App,安装时是全新安装。只会执行 DbHelper 的 onCreate() 方法,直接建立两张表。
  • 之前已经安装过这个 App,安装时时覆盖安装。onCreate() 方法不会再执行了,只会执行 OnUpgrade() 方法。这时候就要判断老版本号是多少,然后在老版本的数据库上进行升级。

第三版需求

第三版的 App 又提出来一个新需求:要给 Book 表和 Category 表建立关联,要在 Book 表中添加一个 category_id 字段。

private static final int VERSION = 3;  // 版本号加 1,现在是 3

// Book 表
private static final String CREATE_TBL_BOOK = "CREATE TABLE Book ("
        + "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        + "name TEXT, "
        + "author TEXT, "
        + "prices INTEGER, "
        + "pages INTEGER, "
        + "category_id INTEGER"  // 增加一个字段
        + ")";

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL(CREATE_TBL_BOOK);  // 直接建立最新的表1
    db.execSQL(CREATE_TBL_CATEGORY)  // 建立表2
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    switch (oldVersion) {
        case 1:
            db.execSQL(CREATE_TBL_CATEGORY)  // 建立新表2
        case 2:
            db.execSQL("ALTER TABLE Book ADD COLUMN category_id INTEGER");  // 修改表1,增加字段
        default:
            break;
    }
}

2019-2-27

posted @ 2019-02-27 16:50  白乾涛  阅读(1121)  评论(0编辑  收藏  举报