美文网首页
【译】处理 Android SQLite onUpgrade()

【译】处理 Android SQLite onUpgrade()

作者: 黄煎饼 | 来源:发表于2018-05-29 15:49 被阅读0次

    原始链接

    Android SQLite OnUpgrade(): 问题

    如果你编写过至少中等规模的安卓app,你很可能实现并利用了一个SQLite数据库。网上到处都是关于如何编写代码来实现的例子。尽管许多例子能够运行,但当需要升级并扩展app的数据库时,它们可能有些麻烦。

    案例app

    在这篇文章中,我们将想象我们有个app需要一张单独的SQLite表。这张表叫做Team。它将有下面的列:Id (int), Name (string), City (string), Mascot (string)。

    标准 OnUpgrade() 教程

    当我第一次开始制作安卓app,我找到了如下的教程(警告:这不是你想要拷贝的代码!):

    public class SQLiteHelper extends SQLiteOpenHelper {
    
        public SQLiteHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
    
        //database values
        private static final String DATABASE_NAME      = "demoApp.db";
        private static final int DATABASE_VERSION      = 1;
        public static final String COLUMN_ID           = "_id";
    
        //team table
        public static final String TABLE_TEAM       = "team";
        public static final String COLUMN_MASCOT    = "mascot";
        public static final String COLUMN_CITY      = "city";
    
        public static final String DATABASE_CREATE_TEAM = "create table "
                + TABLE_TEAM + "(" + COLUMN_ID + " integer primary key autoincrement, "
                + COLUMN_NAME + " string, "
                + COLUMN_MASCOT + " string, "
                + COLUMN_CITY + " string);";
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(DATABASE_CREATE_TEAM);
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_TEAM);
            onCreate(db);
        }
    }
    

    当你处于开发早期阶段时,这份代码还算好用。你可以简单地通过改变创建语句和增加版本号来改变表。问题在于每次升级时,表都会被删除重建。这对于生产环境的app来说并不实际。你不会想要调用:

    db.execSQL("DROP TABLE IF EXISTS " + TABLE_TEAM);
    

    在每次发布一个新版本app时对每张表都这样处理。

    稍好些的例子

    一些意识到问题的开发者可能会谷歌搜索“android onupgrade add column.”不幸的是,截止本文编写为止,前6个结果中的5个都是不好的解决方案!你能在这儿这儿这儿这儿这儿看到这些方案。

    它们都建议修改 OnUpdate() 函数来更好地改变旧版本中的字段参数。但是,它们都有一个问题。这里有一些改进的建议。看看你能不能找到每个例子的问题。

    Bad Example 1
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 
    {
       db.execSQL(ALTER_USER_TABLE_ADD_USER_SOCIETY);
       db.execSQL(ALTER_USER_TABLE_ADD_USER_STREET1);
    }
    

    问题: 这些修改语句将在你每次更新app的时候执行!如果你从版本1升级到版本2,它们将运行并添加列。如果你从2升级到3(且不改变这段代码),这时会再次尝试增加字段并且爆出一个错误。你可能会想只在app改变的时候执行这段代码,但是这也很容易出错,并会使您遇到与示例3相同的错误。

    Bad Example 2
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        // If you need to add a column
        if (newVersion > oldVersion) {
            db.execSQL("ALTER TABLE foo ADD COLUMN new_column INTEGER DEFAULT 0");
        }
    }
    

    问题:这是一个危险的例子,因为许多开发者只是简单地拷贝这段代码。问题在于每次升级时,if 条件都是 true,我们将产生和前一个例子一样的问题。

    Bad Example 3
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        String upgradeQuery = "ALTER TABLE mytable ADD COLUMN mycolumn TEXT";
        if (oldVersion == 1 && newVersion == 2)
             db.execSQL(upgradeQuery);
    }
    

    问题:这个例子有些难找出问题。想象一下如果用户从版本1直接升级到版本3会发生什么。他们将完全错过这段升级代码!这将成为你app的重大问题。

    正确方法

    正如我们所见,我们不想在升级时删除数据库。我们也不想假定用户能每次都严格按顺序进行升级,我们确实希望在跑更新脚本时做一些版本检测。想象我们希望升级我们的app到版本2,使得表增加一个coach字段。而在版本3,我们还希望增加一个stadium字段。 我们可以使用下面的解决方案(你可以安全地使用这段代码。或者改动得更容易阅读和理解后再使用):

    public class SQLiteHelper extends SQLiteOpenHelper {
        public SQLiteHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
    
        //database values
        private static final String DATABASE_NAME      = "demoApp.db";
        private static final int DATABASE_VERSION      = 3;
        public static final String COLUMN_ID           = "_id";
    
        //team table
        public static final String TABLE_TEAM       = "team";
        public static final String COLUMN_MASCOT    = "mascot";
        public static final String COLUMN_CITY      = "city";
        public static final String COLUMN_COACH     = "coach";
        public static final String COLUMN_STADIUM   = "stadium";
    
        private static final String DATABASE_CREATE_TEAM = "create table "
                + TABLE_TEAM + "(" + COLUMN_ID + " integer primary key autoincrement, "
                + COLUMN_NAME + " string, "
                + COLUMN_MASCOT + " string, "
                + COLUMN_COACH + " string, "
                + COLUMN_STADIUM + " string, "
                + COLUMN_CITY + " string);";
    
        private static final String DATABASE_ALTER_TEAM_1 = "ALTER TABLE "
            + TABLE_TEAM + " ADD COLUMN " + COLUMN_COACH + " string;";
    
        private static final String DATABASE_ALTER_TEAM_2 = "ALTER TABLE "
            + TABLE_TEAM + " ADD COLUMN " + COLUMN_STADIUM + " string;";
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(DATABASE_CREATE_TEAM);
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            if (oldVersion < 2) {
                 db.execSQL(DATABASE_ALTER_TEAM_1);
            }
            if (oldVersion < 3) {
                 db.execSQL(DATABASE_ALTER_TEAM_2);
            }
        }
    }
    

    现在,app将执行其需要的更新语句。无论前一个版本是什么,也不管升级到的版本是什么,app将运行合适的语句使得app字段得到正确的更新。 还有一点需要注意的是,你还需要更改在onCreate中的创建语句。所以,如果你在更新中添加了一个字段,请将其添加到onCreate函数中的create语句(对于新用户),并将其添加到onUpgrade函数中的更新语句(对于老用户)。

    你知道处理Android数据库迁移的更好的方法吗?将你的方法邮件给我们,如果我们觉得不错,将把它加入到这篇文章中。另外,如果你正在为你即将推出的app项目寻求帮助,可以查看我们的Android开发服务

    相关文章

      网友评论

          本文标题:【译】处理 Android SQLite onUpgrade()

          本文链接:https://www.haomeiwen.com/subject/btdajftx.html