【问题标题】:Is the onUpgrade method ever called?是否曾经调用过 onUpgrade 方法?
【发布时间】:2017-06-21 15:59:32
【问题描述】:

SQLiteOpenHelperonUpgrade 方法是否被调用过?如果是这样,它是什么时候调用的,由什么调用?如果开发人员没有调用它,那为什么会在那里?该功能到底发生了什么?我已经看到了删除所有表格的示例,但是随后有评论说删除所有表格不是您应该做的。有什么建议么?

【问题讨论】:

    标签: java android android-sqlite


    【解决方案1】:

    对于那些想知道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);
    }
    

    【讨论】:

    • 实际上只有调用 getWriteableDatabase() 时才会调用 onUpgrade。
    • @DoctororDrive - 也调用了getReadableDatabase();都打电话给getDatabaseLocked(boolean writable)
    • 从外部配置文件注入 dbVersion(如@dev.serghini 的示例所示)将通过从测试代码中调用帮助程序来更轻松地测试升级代码。
    【解决方案2】:

    如果您使用的是 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 文档中找到它。
    • 于是出现了一个新问题:“dbVersion”何时更改?开发商控制吗?喜欢应用的“appVersion”吗?
    【解决方案3】:

    当你构造一个版本比打开的数据库版本新的 SQLiteOpenHelper 时调用它。做什么取决于在旧版本和新版本之间对数据库所做的更改。唯一不删除已更改表的情况是,更改不仅仅是添加的列。然后您可以使用 ALTER TABLE 语句将新列添加到表签名中。

    【讨论】:

    • 更改也可能是添加新表,在这种情况下您可能不会删除任何现有表。
    • 但是你怎么会提前知道你会修改一个表呢?或者您是否只需要在每次发布更新时更改方法。
    • 您知道何时更改数据库并在 onUpgrade 中添加另一个案例。因此,当用户更新应用程序时,SQLiteOpenHelper 知道现有数据库已过时并采取相应措施。查看一段 Android 源码以供参考:android.git.kernel.org/?p=platform/frameworks/…
    • 要快速访问@ognian 在您的浏览器中引用的代码,请查看DatabaseHelper on GrepCode
    • @ognian 所以第一次,不会调用任何 onUpgrade 方法,对吧?它将调用构造函数,然后调用 onCreate。只有当我更改版本号时,它才会调用 onUpgrade 而不是 onCreate。对吗?
    【解决方案4】:

    查看所有帖子并运行调试代码,我仍然不清楚何时会看到 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() 之前终止应用程序,您将使用新版本。此后,新的执行永远不会调用 onUpgrade 方法,直到 DATABASE_VERSION 再次增加。 (叹息!现在看起来很明显 :)

    我的建议是在应用的早期阶段添加某种“checkDatabaseVersion()”。或者,如果您创建 SQLiteOpenHelper 对象,请确保在您的应用终止之前调用其中一种方法(getWritableDatabase()、getReadableDatabase() 等)。

    我希望这可以避免其他人同样头疼!...:p

    【讨论】:

      【解决方案5】:

      查看SqliteOpenHelper源代码,我们可以知道onCreate()onUpgrade()onDowngradegetWritableDatabase()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();
              }
          }
      }
      

      【讨论】:

        【解决方案6】:

        当你调用getReadableDatabasegetWritableDatabase时,它实际调用

        深潜:

        您在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);
         }
        

        【讨论】:

          猜你喜欢
          • 2021-11-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-08
          • 1970-01-01
          • 1970-01-01
          • 2015-11-12
          相关资源
          最近更新 更多