Android:升级DB版本,添加新表



我已经为我的应用程序创建了sqlite表,但现在我想添加一个新表到数据库。

我更改了DB版本如下

private static final int DATABASE_VERSION = 2;

和添加字符串创建表

private static final String DATABASE_CREATE_color = 
   "CREATE TABLE IF NOT EXISTS files(color text, incident_id text)";

onCreateonUpgrade如下:

@Override
    public void onCreate(SQLiteDatabase database) {
        database.execSQL(DATABASE_CREATE_incident);
        database.execSQL(DATABASE_CREATE_audio);
        database.execSQL(DATABASE_CREATE_video);
        database.execSQL(DATABASE_CREATE_image);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //drop table and add new tables when version 2 released.
        db.execSQL(DATABASE_CREATE_color);
    }

但是由于某种原因没有创建新表。我做错了什么?

1。关于onCreate()和onUpgrade()

onCreate(..)在新安装应用程序时调用。当应用程序升级和启动并且数据库版本不相同时,调用onUpgrade

2。数据库版本递增

你需要一个构造函数:

MyOpenHelper(Context context) {
   super(context, "dbname", null, 2); // 2 is the database version
}

重要:仅增加应用程序版本是不够的onUpgrade被调用!

3。不要忘记你的新用户!

别忘了加上

database.execSQL(DATABASE_CREATE_color);

设置为onCreate()方法,否则新安装的应用程序将缺少表。

4。如何处理随时间变化的多个数据库更改

当你有连续的应用程序升级,其中几个有数据库升级,你要确保检查oldVersion:

onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
   switch(oldVersion) {
   case 1:
       db.execSQL(DATABASE_CREATE_color);
       // we want both updates, so no break statement here...
   case 2:
       db.execSQL(DATABASE_CREATE_someothertable); 
   }
}

这样,当用户从版本1升级到版本3时,他们将获得两个更新。当用户从版本2升级到版本3时,他们只会得到版本3的更新……毕竟,你不能指望每次发布更新时都有100%的用户进行升级。有时他们会跳过一个更新或12:)

5。在开发

时控制修订号

最后……调用

adb uninstall <yourpackagename>

完全卸载应用程序。当您再次安装时,您保证会击中onCreate,这使您不必在开发时不断增加数据库版本到平流层。

您的代码看起来正确。我的建议是数据库已经认为自己升级了。如果在增加版本号之后执行项目,但在添加execSQL调用之前,测试设备/模拟器上的数据库可能已经认为它处于版本2。

验证这一点的一个快速方法是将版本号更改为3 -如果它在此之后升级,你知道这只是因为你的设备认为它已经升级了。

您可以使用SQLiteOpenHelper的onUpgrade方法。在onUpgrade方法中,您将获得oldVersion作为参数之一。

onUpgrade中使用switch,在每个case中使用版本号来跟踪数据库的当前版本。

最好从oldVersion循环到newVersion,每次增加version 1,然后逐步升级数据库。当使用数据库版本1的人在很长一段时间后将应用程序升级到使用数据库版本7的版本,并且由于某些不兼容的更改而导致应用程序开始崩溃时,这非常有用。

然后数据库中的更新将逐步完成,涵盖所有可能的情况,即合并每个新版本的数据库更改,从而防止应用程序崩溃。

例如:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    switch (oldVersion) {
    case 1:
        String sql = "ALTER TABLE " + TABLE_SECRET + " ADD COLUMN " + "name_of_column_to_be_added" + " INTEGER";
        db.execSQL(sql);
        break;
    case 2:
        String sql = "SOME_QUERY";
        db.execSQL(sql);
        break;
    }
}

@jkschneider的答案是正确的然而,有一个更好的方法。

按照链接https://riggaroo.co.za/android-sqlite-database-use-onupgrade-correctly/

中的描述,在sql文件中为每次更新编写所需的更改

from_1_to_2.sql

ALTER TABLE books ADD COLUMN book_rating INTEGER;

from_2_to_3.sql

ALTER TABLE books RENAME TO book_information;

from_3_to_4.sql

ALTER TABLE book_information ADD COLUMN calculated_pages_times_rating INTEGER;
UPDATE book_information SET calculated_pages_times_rating = (book_pages * book_rating) ;

这些.sql文件将根据数据库的版本在onUpgrade()方法中执行。

DatabaseHelper.java

public class DatabaseHelper extends SQLiteOpenHelper {
    private static final int DATABASE_VERSION = 4;
    private static final String DATABASE_NAME = "database.db";
    private static final String TAG = DatabaseHelper.class.getName();
    private static DatabaseHelper mInstance = null;
    private final Context context;
    private DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }
    public static synchronized DatabaseHelper getInstance(Context ctx) {
        if (mInstance == null) {
            mInstance = new DatabaseHelper(ctx.getApplicationContext());
        }
        return mInstance;
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(BookEntry.SQL_CREATE_BOOK_ENTRY_TABLE);
        // The rest of your create scripts go here.
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.e(TAG, "Updating table from " + oldVersion + " to " + newVersion);
        // You will not need to modify this unless you need to do some android specific things.
        // When upgrading the database, all you need to do is add a file to the assets folder and name it:
        // from_1_to_2.sql with the version that you are upgrading to as the last version.
        try {
            for (int i = oldVersion; i < newVersion; ++i) {
                String migrationName = String.format("from_%d_to_%d.sql", i, (i + 1));
                Log.d(TAG, "Looking for migration file: " + migrationName);
                readAndExecuteSQLScript(db, context, migrationName);
            }
        } catch (Exception exception) {
            Log.e(TAG, "Exception running upgrade script:", exception);
        }
    }
    @Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
    private void readAndExecuteSQLScript(SQLiteDatabase db, Context ctx, String fileName) {
        if (TextUtils.isEmpty(fileName)) {
            Log.d(TAG, "SQL script file name is empty");
            return;
        }
        Log.d(TAG, "Script found. Executing...");
        AssetManager assetManager = ctx.getAssets();
        BufferedReader reader = null;
        try {
            InputStream is = assetManager.open(fileName);
            InputStreamReader isr = new InputStreamReader(is);
            reader = new BufferedReader(isr);
            executeSQLScript(db, reader);
        } catch (IOException e) {
            Log.e(TAG, "IOException:", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    Log.e(TAG, "IOException:", e);
                }
            }
        }
    }
    private void executeSQLScript(SQLiteDatabase db, BufferedReader reader) throws IOException {
        String line;
        StringBuilder statement = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            statement.append(line);
            statement.append("n");
            if (line.endsWith(";")) {
                db.execSQL(statement.toString());
                statement = new StringBuilder();
            }
        }
    }
}

同样的链接中也提供了一个示例项目:https://github.com/riggaroo/AndroidDatabaseUpgrades

处理数据库版本是应用程序开发中非常重要的一部分。我假设您已经有类AppDbHelper扩展SQLiteOpenHelper。当你扩展它时,你需要实现onCreateonUpgrade方法。

  1. onCreateonUpgrade方法调用

    • 应用程序新安装时调用onCreate
    • onUpgrade在app更新时调用。
  2. 组织数据库版本我在类方法中管理版本。创建接口迁移的实现。例如,第一个版本创建MigrationV1类,第二个版本创建MigrationV1ToV2(这是我的命名惯例)


    public interface Migration {
        void run(SQLiteDatabase db);//create tables, alter tables
    }

示例迁移:

public class MigrationV1ToV2 implements Migration{
      public void run(SQLiteDatabase db){
        //create new tables
        //alter existing tables(add column, add/remove constraint)
        //etc.
     }
   }
  • 使用迁移类
  • onCreate:由于onCreate将在新安装应用程序时调用,我们还需要执行所有迁移(数据库版本更新)。所以onCreate看起来像这样:

    public void onCreate(SQLiteDatabase db){
            Migration mV1=new MigrationV1();
           //put your first database schema in this class
            mV1.run(db);
            Migration mV1ToV2=new MigrationV1ToV2();
            mV1ToV2.run(db);
            //other migration if any
      }
    

    onUpgrade:此方法将在应用程序已经安装并更新到新应用程序版本时调用。如果应用程序包含任何数据库更改,则将所有数据库更改放入新的迁移类和增量数据库版本中。

    例如,假设用户安装了数据库版本为1的应用程序,现在数据库版本更新为2(所有模式更新保存在MigrationV1ToV2中)。现在当应用程序升级时,我们需要通过在MigrationV1ToV2中应用数据库模式更改来升级数据库,如下所示:

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion < 2) {
            //means old version is 1
            Migration migration = new MigrationV1ToV2();
            migration.run(db);
        }
        if (oldVersion < 3) {
            //means old version is 2
        }
    }
    

    注意:所有数据库模式的升级(在onUpgrade中提到)都应该在onCreate

    中执行。

    在第一个db版本中,表如下:

       String createUserTable = "CREATE TABLE IF NOT EXISTS "+ user + " (" +
               USER_ID + " INTEGER PRIMARY KEY, " +
               USER_NAME + " TEXT NOT NULL, " +
               USER_FAMILY + " TEXT NOT NULL, " +
               USER_LIVING_STATUS + " INTEGER DEFAULT 0);";   
    
    在第二个版本中,我们添加了一个名为color的新列,这就是为什么升级代码如下:
    @Override   
    public void onUpgrade(SQLiteDatabase mydb, int oldVersion, int newVersion) {
       
           if (newVersion > oldVersion) {
         
           /******************************** delete the last table and create new table with new columns  and copy    the former to new one *********************************/
           
           // here we added color column to new table
           mydb.execSQL("CREATE TABLE IF NOT EXISTS user_tmp ("
                   + "user_id integer, user_name text, user_family text, user_color text,user_living_status integer);");
           mydb.execSQL("INSERT INTO user_tmp(user_id,user_name,user_family,user_living_status) SELECT * FROM user");
           mydb.execSQL("drop table user;");
           mydb.execSQL("ALTER TABLE user_tmp RENAME TO user");
           
       }
           }
    

    最新更新