onUpgrade方法会被调用吗?

37

SQLiteOpenHelperonUpgrade方法是否会被调用?如果会,那么它是在什么时候被调用的?由谁调用?如果开发人员不调用它,那么它为什么存在?这个函数到底做了什么?我看到一些示例中它会删除所有表,但是有一个注释说不应该这样做。有什么建议吗?

6个回答

37

如果你想知道onUpgrade()方法被调用的确切时刻,那就是在调用getReadableDatabase()getWriteableDatabase()方法时。

对于那些不清楚它如何被触发的人,答案是:当提供给SqLiteOpenHelper构造函数的数据库版本更新时,它就会被触发。以下是一个例子:

public class dbSchemaHelper extends SQLiteOpenHelper {

private String sql;
private final String D_TAG = "FundExpense";
//update this to get onUpgrade() method of sqliteopenhelper class called
static final int DB_VERSION = 2; 
static final String DB_NAME = "fundExpenseManager";

public dbSchemaHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
    // TODO Auto-generated constructor stub
}

现在开始讲解如何使用onUpgrade()方法

@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
    sql = "ALTER TABLE " + fundExpenseSchema.Expense.TABLE_NAME + " ADD COLUMN " + fundExpenseSchema.Expense.FUNDID + " INTEGER";
    arg0.execSQL(sql);
}

3
仅当调用getWriteableDatabase()时,才会实际调用onUpgrade。 - Yaroslav Mytkalyk
1
@DoctororDrive - 也被称为 getReadableDatabase(); 两者都调用了 getDatabaseLocked(boolean writable) - CJBS
从外部配置文件中注入dbVersion(如@dev.serghini所示的示例)将使得通过测试代码调用助手程序更容易,从而更方便地测试升级代码。 - Basel Shishani

29

如果你正在使用SQLiteOpenHelper,当你改变数据库版本时,onUpgrade方法将被调用。这要求实现中有一个额外的限制条件,即数据库名称必须保持不变。

Old Version:
dbName = "mydb.db"
dbVersion = 1

New Version:
dbName = "mydb.db"
dbVersion = 2

在内容提供者的onCreate方法中,您创建一个使用这些参数的SQLiteOpenHelper实例。您的SQLiteOpenHelper实现应如下所示:

public static final class MySQLiteOpenHelper extends SQLiteOpenHelper {

        public MySQLiteOpenHelper(Context context, int dbVersion, String dbName) {
            super(context, dbName, null, dbVersion);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            //Code to create your db here
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // Code to upgrade your db here
        }

}

@dev.serghini,你在哪里找到的信息表明仅更改数据库版本名称就会触发“onUpgrade”?我需要官方确认,并且我在该方法的官方Java文档中找不到相关内容。 - sandalone
2
那么接下来就出现了一个新问题:什么时候会更改“dbVersion”?开发者是否控制它,就像应用程序的“appVersion”一样? - sports

22

当您使用版本号高于已打开数据库版本的SQLiteOpenHelper构造函数时,会调用它。操作取决于旧版本和新版本之间所做的数据库更改。当更改仅仅是添加列时,唯一不需要删除更改表的情况就是使用ALTER TABLE语句将新列添加到表签名中。


4
如果需要添加新的表格,你也可以进行更改,这种情况下你不需要删除任何已存在的表格。 - CommonsWare
3
你如何事先知道你将要修改一个表格呢?或者说每次发布更新时都必须更改方法? - Mohit Deshpande
3
当你改变数据库并在onUpgrade中添加另一个case时,当用户更新应用程序时,SQLiteOpenHelper会知道现有的数据库已过时,并采取相应的操作。可参考以下Android源代码:http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java - ognian
2
要快速访问 @ognian 在浏览器中引用的代码,请查看 GrepCode 上的 DatabaseHelper - hotshot309
@ognian 所以第一次启动时,不会调用任何onUpgrade方法,对吧?它将调用构造函数,然后调用onCreate。只有当我更改版本号时,它才会调用onUpgrade而不是onCreate。对吧? - Kraken

5

在审查所有帖子并运行调试代码后,我仍然不清楚何时会调用onUpgrade。我开始认为Android有严重的缺陷。

这个页面上的信息让我找到了最终的解决方案。感谢所有贡献者!

这对我解决了问题...

public class DatabaseHelper extends SQLiteOpenHelper {
    public static String TAG = DatabaseHelper.class.getName();
    private static final int DATABASE_VERSION = 42;
    private static final String DATABASE_NAME = "app_database";
    private static final String OLD_TABLE = "old_and_useless";

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion ) {
        if( newVersion > oldVersion) {
            Log.d( TAG, "cool! you noticed." );

            db.execSQL( "DROP TABLE IF EXISTS " + OLD_TABLE );
            // other calls like onCreate if necessary

        } else {
            Log.d( TAG, "Hey! didn't you see me?" );
        }

    }

    public void checkDatabaseVersion() {
        SQLiteDatabase db = this.getWritableDatabase();

        // if the DATABASE_VERSION is newer
        //    onUpgrade is called before this is reached
    }


    // other code removed for readability...
}

确实,调用getWritableDatabase()和getReadableDatabase()会导致onUpgrade方法被调用。由于这些方法符合我的需求,我没有检查其他方法。

继续阅读,有一个重要提示...

在我最初的Activity中,这段代码让我恍然大悟,我终于意识到在调试期间数据库版本正在更新...呃!

DatabaseHelper dbHelper = new DatabaseHelper( this );
dbHelper.checkDatabaseVersion();

注意:调用DatabaseHelper构造函数会更新数据库版本。
在构造函数调用后,数据库将被标记为新版本。在调用getWritableDatabase()或getReadableDatabase()之前杀死应用程序,您就可以使用新版本了。此后,只有在再次增加DATABASE_VERSION时,新的执行才不会调用onUpgrade方法。(哎呀!现在看起来显然得令人发笑 :))
我的建议是在应用程序的早期阶段添加某种“checkDatabaseVersion()”功能。或者,如果您创建一个SQLiteOpenHelper对象,请确保在应用程序死亡之前调用其中一个方法(getWritableDatabase(),getReadableDatabase()等)。
我希望这能帮助其他人避免同样的困惑!... :p

3

查看SqliteOpenHelper源代码,我们可以知道onCreate()onUpgrade()onDowngrade()方法会在getWritableDatabase()getReadableDatabase()方法中被调用。

public SQLiteDatabase getWritableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(true);
    }
}
public SQLiteDatabase getReadableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(false);
    }
}

private SQLiteDatabase getDatabaseLocked(boolean writable) {
    if (mDatabase != null) {
        if (!mDatabase.isOpen()) {
            // Darn!  The user closed the database by calling mDatabase.close().
            mDatabase = null;
        } else if (!writable || !mDatabase.isReadOnly()) {
            // The database is already open for business.
            return mDatabase;
        }
    }
          . . . . . .  

        final int version = db.getVersion();
        if (version != mNewVersion) {
            if (db.isReadOnly()) {
                throw new SQLiteException("Can't upgrade read-only database from version " +
                        db.getVersion() + " to " + mNewVersion + ": " + mName);
            }

            db.beginTransaction();
            try {
                if (version == 0) {
                    onCreate(db);
                } else {
                    if (version > mNewVersion) {
                        onDowngrade(db, version, mNewVersion);
                    } else {
                        onUpgrade(db, version, mNewVersion);
                    }
                }
                db.setVersion(mNewVersion);
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }

        onOpen(db);

        if (db.isReadOnly()) {
            Log.w(TAG, "Opened " + mName + " in read-only mode");
        }

        mDatabase = db;
        return db;
    } finally {
        mIsInitializing = false;
        if (db != null && db != mDatabase) {
            db.close();
        }
    }
}

2
当你调用`getReadableDatabase`或者`getWritableDatabase`时,它实际上被称为。
深入了解:
你在`SQLiteOpenHelper`的构造函数中传递版本号并存储在一个名为`mNewVersion`的变量中。这就是全部。此时不会发生任何事情。
每次调用`getReadableDatabase`或`getWritableDatabase`时,它都会调用一个名为`getDatabaseLocked`的方法。此方法将获取数据库的现有版本号,并将其与`mNewVersion`进行比较。
1. 如果具有给定名称的数据库不存在,则将调用`onCreate`。 2. 如果新版本大于旧版本,则将调用`onUpgrade`。 3. 如果新版本低于现有版本,则会抛出异常。 4. 如果它们相等,它将继续打开数据库。
在`onCreate`和`onUpgrade`中应该写什么?
`onCreate`应该包含首次创建模式的代码。
第一次可以将`onUpgrade`留空,因为第一次不会调用它。当您想要在以后更改表结构时,应将代码放在此处。
SQLiteOpenHelper.java(源代码)
public SQLiteDatabase getWritableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(true);
    }
}

 public SQLiteDatabase getReadableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(false);
    }
}

private SQLiteDatabase getDatabaseLocked(boolean writable) {
   .
   .

     final int version = db.getVersion();

        if (version != mNewVersion) {
            if (db.isReadOnly()) {
                throw new SQLiteException("Can't upgrade read-only database from version " +
                        db.getVersion() + " to " + mNewVersion + ": " + mName);
            }

            db.beginTransaction();
            try {
                if (version == 0) {
                    onCreate(db);
                } else {
                    if (version > mNewVersion) {
                        onDowngrade(db, version, mNewVersion);
                    } else {
                        onUpgrade(db, version, mNewVersion);
                    }
                }
                db.setVersion(mNewVersion);
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
       }

       onOpen(db);
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接