Android SQLite (二.數(shù)據(jù)庫創(chuàng)建,升級及降級)

一.SQLite簡介和常用語法
二.數(shù)據(jù)庫創(chuàng)建,升級及降級

上篇文章簡介和常用語法介紹了SQLite數(shù)據(jù)庫的基本信息和一些常用的語法操作,本篇文章主要介紹Android開發(fā)過程中SQLite數(shù)據(jù)庫的創(chuàng)建使用和常見問題處理。

一.SQLiteOpenHelper介紹

對于Android平臺來說,我們可以使用系統(tǒng)提供的API輕松實(shí)現(xiàn)對SQLite數(shù)據(jù)庫的操作
Android提供SQLiteOpenHelper類以便我們創(chuàng)建操作數(shù)據(jù)庫。通常情況下我們會新建一個SQLiteOpenHelper的子類作為一個數(shù)據(jù)庫的操作類。
數(shù)據(jù)庫操作類示例 :

DatabaseHelper

public class DatabaseHelper extends SQLiteOpenHelper {

    /**
     * 數(shù)據(jù)庫名稱
     */
    private static final String DATABASE_NAME = "account.db";
    /**
     * 數(shù)據(jù)庫版本-升級用
     */
    private static final int DATABASE_VERSION = 1;

    public DatabaseHelper(Context context) {
        //CursorFactory設(shè)置為null,使用默認(rèn)值
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * 數(shù)據(jù)庫第一次創(chuàng)建時調(diào)用
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS account " +
                "(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER)");
    }

    /**
     * @param db         數(shù)據(jù)庫實(shí)際操作類
     * @param oldVersion 舊版本
     * @param newVersion 新版本
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

二.數(shù)據(jù)庫創(chuàng)建-構(gòu)造方法

數(shù)據(jù)庫的創(chuàng)建以及版本,是由SQLiteOpenHelper的構(gòu)造方法參數(shù)決定的。
SQLiteOpenHelper 有三個構(gòu)造構(gòu)造方法:

    /**
     * @param context 用來打開或者創(chuàng)建數(shù)據(jù)庫
     * @param name 數(shù)據(jù)庫文件名稱或者傳null為一個內(nèi)存數(shù)據(jù)庫
     * @param factory 用來創(chuàng)建cursor對象或者傳null使用默認(rèn)的
     * @param version 數(shù)據(jù)庫版本號 (從1開始);如果數(shù)據(jù)庫版本比當(dāng)前版舊,onUpgrade方法會被調(diào)用來更新數(shù)據(jù)庫,
       如果比當(dāng)前新onDowngrade方法會被調(diào)用來降級數(shù)據(jù)庫
     */
    public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
        this(context, name, factory, version, null);
    }

    /**
     * @param errorHandler DatabaseErrorHandler數(shù)據(jù)庫損壞時回調(diào).
     */
    public SQLiteOpenHelper(Context context, String name, CursorFactory factory, 
        int version, DatabaseErrorHandler errorHandler) {
        this(context, name, factory, version, 0, errorHandler);
    }

   /**
     * @param minimumSupportedVersion 支持調(diào)用onUpgrade更新到version版本的最版本號,
       如果當(dāng)前版本比最小支持的版本還下數(shù)據(jù)庫就會刪除然后重新創(chuàng)建version版本的數(shù)據(jù)庫。
       在刪除前onBeforeDelete會被調(diào)用我們可以在此方法中做一些處理。minimumSupportedVersion 默認(rèn)為0
     * @hide
     */
    public SQLiteOpenHelper(Context context, String name, CursorFactory factory,
    int version, int minimumSupportedVersion, DatabaseErrorHandler errorHandler) {
        if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + 
        version);

        mContext = context;
        mName = name;
        mFactory = factory;
        mNewVersion = version;
        mErrorHandler = errorHandler;
        mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
    }

最終都會調(diào)用第三個構(gòu)造方法,方法中判斷如果version 小于1會拋出異常,所以Android SQLite數(shù)據(jù)庫版本必須從1開始。實(shí)際上最后一個構(gòu)造方法被@hide注解了,所以一般情況下在子類中我們是沒有辦法主動調(diào)用的,只能調(diào)用前兩個構(gòu)造方法,minimumSupportedVersion 也始終是0。

三.數(shù)據(jù)庫首次創(chuàng)建回調(diào)-onCreate方法

數(shù)據(jù)庫首次創(chuàng)建的時候onCreate方法會被執(zhí)行。表的創(chuàng)建和初始化需要在方法中進(jìn)行。這個是抽象方法,實(shí)體類中必須實(shí)現(xiàn)。實(shí)現(xiàn)很簡單就是做一些創(chuàng)建表的操作。
示例:

   /**
     * 數(shù)據(jù)庫第一次創(chuàng)建時調(diào)用
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS account " +
                "(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER)");
    }

四.數(shù)據(jù)庫的升級-onUpgrade方法

   /**
     * @param db The database.
     * @param oldVersion 舊數(shù)據(jù)庫版本
     * @param newVersion 新數(shù)據(jù)庫版本
     */
    public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);

當(dāng)數(shù)據(jù)庫升級時(當(dāng)前數(shù)據(jù)庫版本大于已創(chuàng)建的數(shù)據(jù)庫版本)會執(zhí)行此方法,首次創(chuàng)建時不會執(zhí)行。這是個抽象方法,在我們的實(shí)體類中必須實(shí)現(xiàn)。我們可以在這個方法中根據(jù)數(shù)據(jù)庫版本做一些刪除表,創(chuàng)建表,新增表字段或者重命名表等操作保證數(shù)據(jù)庫與最新版本數(shù)據(jù)庫同步。

常用數(shù)據(jù)庫升級操作:

數(shù)據(jù)庫的升級一般有三種方式如下:

1.onCreate方法始終保證是最新的,在onUpgrade方法中刪除所有已存在表,在調(diào)用onCreate中創(chuàng)建表的方法重新創(chuàng)建所有表,保證表是最新的。(當(dāng)然如果你想保存數(shù)據(jù)也可以先將數(shù)據(jù)保存到內(nèi)存再插入,如果表很多或者數(shù)據(jù)量很大的是不建議怎么操作的,非常影響性能)

2.onCreate方法始終保證是最新的,再onUpgrade方法中根據(jù)版本號判斷執(zhí)行創(chuàng)建新表或者更新字段等操作

版本號需要 判斷上下限:

上限(為了再從當(dāng)前版本x更新到新版本y時不重復(fù)執(zhí)行更新操作):假如我們要升級到新版本x,是我們的當(dāng)前版本x,需要小于當(dāng)前版本號x。

下限:如果是創(chuàng)建新表下限就是當(dāng)前版本之前的所有版本,如果是更新表字段或者重命名表下限是表創(chuàng)建的版本需要大于等于下限版本

  • 舉個栗子:
  • 2.1. 在上面的示例我們創(chuàng)建了版本1數(shù)據(jù)庫,onCreate方法創(chuàng)建了表account,現(xiàn)在需要在表account中新增字段subAccountId并新增一個表subAccount。

按照上面的方式我們需要更新表版本號為假設(shè)為2(可以為更大的,不要求連續(xù),連續(xù)方便之后的版本更新迭代),在onCreate方法中修改原來創(chuàng)建表account的SQL新增字段subAccountId,同時新增創(chuàng)建表subAccount的SQL并執(zhí)行。
然后在onUpgrade中判斷oldVersion版本號大于等于原版本1小于現(xiàn)版本2,執(zhí)行新增字段和新增表SQL。
具體代碼如下:

public class DatabaseHelper extends SQLiteOpenHelper {

    /**
     * 數(shù)據(jù)庫名稱
     */
    private static final String DATABASE_NAME = "account.db";
    /**
     * 數(shù)據(jù)庫版本-升級用
     */
    private static final int DATABASE_VERSION = 2;

    public DatabaseHelper(Context context) {
        //CursorFactory設(shè)置為null,使用默認(rèn)值
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * 數(shù)據(jù)庫第一次創(chuàng)建時調(diào)用
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS account " +
                "(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER," +
                "subAccountId VARCHAR default '')");

        db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
                "(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER)");
    }

    /**
     * @param db         數(shù)據(jù)庫實(shí)際操作類
     * @param oldVersion 舊版本
     * @param newVersion 新版本
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion >= 1 && oldVersion < 2) {
            db.execSQL("ALTER TABLE account ADD COLUMN subAccountId VARCHAR default ''");
            db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
                    "(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER)");
        }
    }

}
  • 2.2 假設(shè)這個時候,還需要升級數(shù)據(jù)庫到版本3,在表subAccount 中新增個字段userName。
    onCreate方法里修改就不說了創(chuàng)建表subAccount 加上userName字段就行。onUpgrade方法中怎么判斷版本號,按照上面的說明可知上限是小于3,下限則是大于等于表創(chuàng)建的版本,subAccount 表創(chuàng)建的版本是2所以需要大于等于2,而不是大于等于初始版本。
    具體代碼如下:
   /**
     * 數(shù)據(jù)庫版本-升級用
     * 版本2:新增數(shù)據(jù)庫subAccount 
     */
    private static final int DATABASE_VERSION = 3;

    public DatabaseHelper(Context context) {
        //CursorFactory設(shè)置為null,使用默認(rèn)值
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * 數(shù)據(jù)庫第一次創(chuàng)建時調(diào)用
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        createAccountTable(db);
        createSubAccountTable(db);
    }
    
   /**
     * 創(chuàng)建subAccount表(最好封裝下,保證onCreate和onUpgrade方法中創(chuàng)建的都是最新表)
     */
    private void createSubAccountTable(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
                "(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER," +
                "userName VARCHAR default '')");
    }

    private void createAccountTable(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS account " +
                "(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER," +
                "subAccountId VARCHAR default '')");
    }

    /**
     * @param db         數(shù)據(jù)庫實(shí)際操作類
     * @param oldVersion 舊版本
     * @param newVersion 新版本
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion >= 1 && oldVersion < 2) {
            db.execSQL("ALTER TABLE account ADD COLUMN subAccountId VARCHAR default ''");
            createSubAccountTable(db);
        }

        if (oldVersion >= 2 && oldVersion < 3) {
            db.execSQL("ALTER TABLE subAccount ADD COLUMN userName VARCHAR default ''");
        }
    }
}
  • 升級測試說明:
    直接安裝版本3數(shù)據(jù)庫的APP——走onCreate方法創(chuàng)建最新表
    安裝版本2--升級版本3APP——onUpgrade只會走升級subAccount 表的邏輯
    安裝版本1--升級版本3APP——onUpgrade只會走升級account 表的邏輯和創(chuàng)建表subAccount 的邏輯,創(chuàng)建表subAccount 和onCreate中執(zhí)行的是同義SQL,創(chuàng)建的都是最新表。

這種方式更新數(shù)據(jù)庫一定要注意表的創(chuàng)建版本,所以必要的注釋還是要詳細(xì)點(diǎn)。

3.第三種方式和第二種基本是一樣,只是在onUpgrade方法中一步步往上升級,已上面2.2例子說明,初始版本為1,升級到版本3,先升級到版本2再升級到版本3這樣就不需要判斷oldVersion 版本號的上下限了。
使用這種方式升級,onCreate中可以是最新表創(chuàng)建(3.1示例),也可以只進(jìn)行最初版本的表創(chuàng)建然后調(diào)用onUpgrade方法升級數(shù)據(jù)庫(3.2示例)

  • 3.1 onCreate中最新的數(shù)據(jù)表,onUpgrade方法中根據(jù)版本號一步步往上升級
    代碼如下:
 /**
     * 數(shù)據(jù)庫名稱
     */
    private static final String DATABASE_NAME = "account.db";

    /**
     * 數(shù)據(jù)庫版本-升級用
     */
    private static final int DATABASE_VERSION = 3;

    public DatabaseHelper3(Context context) {
        //CursorFactory設(shè)置為null,使用默認(rèn)值
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * 數(shù)據(jù)庫第一次創(chuàng)建時調(diào)用
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        createAccountTable(db);
        createSubAccountTable(db);
    }

    private void createSubAccountTable(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
                "(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER," +
                "userName VARCHAR default '')");
    }

    private void createAccountTable(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS account " +
                "(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER," +
                "subAccountId VARCHAR default '')");
    }

    /**
     * @param db         數(shù)據(jù)庫實(shí)際操作類
     * @param oldVersion 舊版本
     * @param newVersion 新版本
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        int version = oldVersion;
        if (version == 1){
            db.execSQL("ALTER TABLE account ADD COLUMN subAccountId VARCHAR default ''");
            db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
                    "(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER)");
            version = 2;
        }
        if (version == 2){
            db.execSQL("ALTER TABLE subAccount ADD COLUMN userName VARCHAR default ''");
        }
    }
  • 3.2 onCreate中創(chuàng)建最初版本的表然后調(diào)用onUpgrade方法傳入oldVersion為最初版本,onUpgrade方法中根據(jù)版本號一步步往上升級,需要判斷下版本號oldVersion>=newVersion時不做處理(即初始版本不處理)
    代碼如下:
/**
     * 數(shù)據(jù)庫名稱
     */
    private static final String DATABASE_NAME = "account.db";

    /**
     * 最初版本
     */
    private static final int FIRST_DATABASE_VERSION = 1;

    /**
     * 數(shù)據(jù)庫版本-升級用
     */
    private static final int DATABASE_VERSION = 3;

    public DatabaseHelper2(Context context) {
        //CursorFactory設(shè)置為null,使用默認(rèn)值
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * 數(shù)據(jù)庫第一次創(chuàng)建時調(diào)用
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS account " +
                "(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER)");
        onUpgrade(db,FIRST_DATABASE_VERSION,DATABASE_VERSION);
    }

    /**
     * @param db         數(shù)據(jù)庫實(shí)際操作類
     * @param oldVersion 舊版本
     * @param newVersion 新版本
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion >= newVersion){
            return;
        }
        int version = oldVersion;
        if (version == 1){
            db.execSQL("ALTER TABLE account ADD COLUMN subAccountId VARCHAR default ''");
            db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
                    "(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER)");
            version = 2;
        }
        if (version == 2){
            db.execSQL("ALTER TABLE subAccount ADD COLUMN userName VARCHAR default ''");
        }
    }

或者直接判斷小于也是可以的,思路和上面是完全一樣的,也是一步步往上升級,僅僅是寫法不一樣,隨變選一個寫法就行,示例代碼如下:

 /**
     * @param db         數(shù)據(jù)庫實(shí)際操作類
     * @param oldVersion 舊版本
     * @param newVersion 新版本
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion >= newVersion){
            return;
        }
        if (oldVersion < 2){
            db.execSQL("ALTER TABLE account ADD COLUMN subAccountId VARCHAR default ''");
            db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
                    "(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER)");
        }
        if (version < 3){
            db.execSQL("ALTER TABLE subAccount ADD COLUMN userName VARCHAR default ''");
        }
    }

總結(jié):
以上三種方式均可以實(shí)現(xiàn)數(shù)據(jù)庫版本的升級。
如果數(shù)據(jù)庫版本升級不需要保存舊表數(shù)據(jù)可以選擇方式1升級數(shù)據(jù)庫,刪除所有表重新創(chuàng)建。
如果數(shù)據(jù)庫版本升級需要保存數(shù)據(jù)庫表數(shù)據(jù),則可以選擇方式二或者方式三。
個人比較建議選擇方式二,新安裝APP創(chuàng)建數(shù)據(jù)庫不會做很多多余的操作且邏輯也比較清晰。
當(dāng)然大家可以根據(jù)各自的需求選擇,如果數(shù)據(jù)庫修改不是很頻繁而且修改較多的情況下,方式三也不失為一個好的選擇,代碼邏輯很清晰。

五.數(shù)據(jù)庫降級-onDowngrade方法

 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        throw new SQLiteException("Can't downgrade database from version " +
                oldVersion + " to " + newVersion);
    }
  • 降級方法,當(dāng)數(shù)據(jù)庫降級時(當(dāng)前數(shù)據(jù)庫版本小于已創(chuàng)建的數(shù)據(jù)庫版本)執(zhí)行此方法。
  • onDowngrade方法是有默認(rèn)實(shí)現(xiàn)的,我們可以看到默認(rèn)實(shí)現(xiàn)就是拋出一個降級異常,所以如果我們不重寫此方法,APP 數(shù)據(jù)庫是沒法往下降級的,安裝的時候會直接崩潰。除非你卸載了重裝,這樣相當(dāng)于首次創(chuàng)建數(shù)據(jù)庫,走onCreate方法。
  • 我們可以根據(jù)實(shí)際需求來決定是否需要重寫onDowngrade方法,如果應(yīng)用降級覆蓋安裝不涉及數(shù)據(jù)庫降級或者是不允許降級就不需要重寫此方法,強(qiáng)制用戶安裝最新的。如果有需要則可以重寫此方法,一般操作為刪除所有表再調(diào)用onCreate方法重寫創(chuàng)建表。SQLite數(shù)據(jù)庫表是不能刪減字段的,這樣做舊版本表可能有冗余字段,但這不影響舊版本操作。

舉個栗子:

@Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        dropTable(db,"account");
        dropTable(db,"subAccount");
        onCreate(db);
    }

    /**
     * 刪除表
     * @param db        數(shù)據(jù)庫
     * @param tableName 表名
     */
    public void dropTable(SQLiteDatabase db, String tableName) {
        if (TextUtils.isEmpty(tableName)) {
            return;
        }
        db.execSQL("drop table " + tableName);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容