2011-11-15 39 views
103

我已經爲我的應用程序創建了sqlite表,但現在我想向數據庫添加一個新表。Android:升級數據庫版本並添加新表

我改變了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); 

    } 

但由於某些原因,新表格沒有被創建。我究竟做錯了什麼?

+0

[This](https://stackoverflow.com/a/3424444/3681880)是另一個有趣的解決方案,但到目前爲止,我見過的最強大的版本是[here](https://riggaroo.co.za/Android的源碼數據庫 - 使用 - onupgrade-正確/)。 – Suragch

回答

243

1.關於的onCreate()和onUpgrade()每當應用程序是新安裝的

onCreate(..)被調用。每當應用程序升級並啓動並且數據庫版本不相同時調用onUpgrade

2.遞增DB版本

你需要一個構造,如:

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,這使你不必在開發時不斷地將數據庫版本增加到平流層中。

+6

關於將調用添加到'onCreate()' – Greyson

+4

關於#4:使用傳遞的'oldVersion'參數不是一個好主意嗎?如果任何升級語句都是可重複的,則最終可能會在大多數最新的數據庫上重複它們。如果其中一個陳述是截斷表格,那將是非常糟糕的。 – Greyson

+3

@格雷森:好點!老實說,我從來沒有真正想過它,我感到有點愚蠢。有時候,我認爲我們養成了使用我們想要的論據而忽略其餘部分的習慣! – jkschneider

9

您的代碼看起來是正確的。我的建議是數據庫已經認爲它已經升級了。如果在增加版本號後執行項目,但在添加execSQL調用之前,測試設備/仿真器上的數據庫可能已經認爲它是版本2.

驗證此問題的快速方法是更改​​版本數字爲3 - 如果在此之後升級,您知道這僅僅是因爲您的設備認爲它已經升級。

+1

謝謝。 Dat幫助:)更改爲3後,它的工作:) –

+0

然後,正如所料,您的代碼是好的;而不是當它逐步運行時。請記住像jkschneider指出的那樣將表創建添加到'onCreate()'。 – Greyson

2

您可以使用SQLiteOpenHelper的onUpgrade方法。在onUpgrade方法中,您將oldVersion作爲參數之一。

onUpgrade中使用一個switch並在每個case s使用版本號來跟蹤當前版本的數據庫。

最好從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; 
    } 

} 
+0

如果你刪除這些break語句,你將不需要循環 –

1

@ jkschneider的答案是正確的。但是有一個更好的方法。

如在鏈接https://riggaroo.co.za/android-sqlite-database-use-onupgrade-correctly/

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

描述收件在每次更新一個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()方法根據數據庫的版本來執行。 https://github.com/riggaroo/AndroidDatabaseUpgrades

+0

我剛來這裏寫下相同的建議。我很高興你已經做到了。人們應該閱讀你鏈接的文章。這也是[Android SQLiteAssetHelper](https://github.com/jgilfelt/android-sqlite-asset-helper)推薦的升級。這也是[CL。](https://stackoverflow.com/users/11654/cl)(* Stack Overflow上的SQLite專家)[推薦](https://stackoverflow.com/a/42361543/ 3681880)。 – Suragch

0

處理數據庫的版本是應用程序開發的非常重要的組成部分:

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(); 
      } 
     } 
    } 
} 

一個例子項目是在同一鏈路還提供。我假設你已經有AppDbHelper類延伸SQLiteOpenHelper。當你繼承它,你將需要實現onCreateonUpgrade方法。

  1. onCreateonUpgrade方法稱爲

    • onCreate當新安裝的應用程序調用。
    • onUpgrade當應用程序更新時調用。
  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

    執行
    相關問題