閱讀370 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Android 數據庫升級解決方案

[-]
  1. 總體思路
  2. 得到數據庫表的列名
  3. 數據庫升級的意義
    1. 定義數據庫版本
    2. 如何寫升級邏輯
    3. 注意
    4. 如何保證數據不丟失
  4. SQL語句

請考慮如下情況:

在數據庫升級時,不同版本的數據庫,他們定義的表結構完全可能是不一樣的,比如V1.0的表A有10個column,而在V1.1的表A有12個colum,在升級時,表A增加了兩列,此時我們應該怎麼做呢。


總體思路

1,將表A重命名,改了A_temp。

2,創建新表A。

3,將表A_temp的數據插入到表A。

下麵代碼列出了更新表的實現,upgradeTables,給定表名,更新的列名,就可以實現數據庫表的更新。

  1. /** 
  2.  * Upgrade tables. In this method, the sequence is: 
  3.  * <b> 
  4.  * <p>[1] Rename the specified table as a temporary table. 
  5.  * <p>[2] Create a new table which name is the specified name. 
  6.  * <p>[3] Insert data into the new created table, data from the temporary table. 
  7.  * <p>[4] Drop the temporary table. 
  8.  * </b> 
  9.  * 
  10.  * @param db The database. 
  11.  * @param tableName The table name. 
  12.  * @param columns The columns range, format is "ColA, ColB, ColC, ... ColN"; 
  13.  */  
  14. protected void upgradeTables(SQLiteDatabase db, String tableName, String columns)  
  15. {  
  16.     try  
  17.     {  
  18.         db.beginTransaction();  
  19.   
  20.         // 1, Rename table.  
  21.         String tempTableName = tableName + "_temp";  
  22.         String sql = "ALTER TABLE " + tableName +" RENAME TO " + tempTableName;  
  23.         execSQL(db, sql, null);  
  24.   
  25.         // 2, Create table.  
  26.         onCreateTable(db);  
  27.   
  28.         // 3, Load data  
  29.         sql =   "INSERT INTO " + tableName +  
  30.                 " (" + columns + ") " +  
  31.                 " SELECT " + columns + " FROM " + tempTableName;  
  32.   
  33.         execSQL(db, sql, null);  
  34.   
  35.         // 4, Drop the temporary table.  
  36.         execSQL(db, "DROP TABLE IF EXISTS " + tempTableName, null);  
  37.   
  38.         db.setTransactionSuccessful();  
  39.     }  
  40.     catch (SQLException e)  
  41.     {  
  42.         e.printStackTrace();  
  43.     }  
  44.     catch (Exception e)  
  45.     {  
  46.         e.printStackTrace();  
  47.     }  
  48.     finally  
  49.     {  
  50.         db.endTransaction();  
  51.     }  
  52. }  

得到數據庫表的列名

我們可以通過SQL表得到表的列名。 這裏需要注意的一點,int columnIndex = c.getColumnIndex("name"); 這裏根據name去取得index。 

  1. protected String[] getColumnNames(SQLiteDatabase db, String tableName)  
  2. {  
  3.     String[] columnNames = null;  
  4.     Cursor c = null;  
  5.   
  6.     try  
  7.     {  
  8.         c = db.rawQuery("PRAGMA table_info(" + tableName + ")"null);  
  9.         if (null != c)  
  10.         {  
  11.             int columnIndex = c.getColumnIndex("name");  
  12.             if (-1 == columnIndex)  
  13.             {  
  14.                 return null;  
  15.             }  
  16.   
  17.             int index = 0;  
  18.             columnNames = new String[c.getCount()];  
  19.             for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext())  
  20.             {  
  21.                 columnNames[index] = c.getString(columnIndex);  
  22.                 index++;  
  23.             }  
  24.         }  
  25.     }  
  26.     catch (Exception e)  
  27.     {  
  28.         e.printStackTrace();  
  29.     }  
  30.     finally  
  31.     {  
  32.         closeCursor(c);  
  33.     }  
  34.   
  35.     return columnNames;  
  36. }  


upgradeTables方法應該是在onUpgrade方法中去調用。


數據庫升級的意義

在應用程序開發的過程中,數據庫的升級是一個很重要的組成部分(如果用到了數據庫),因為程序可能會有V1.0,V2.0,當用戶安裝新版本的程序後,必須要保證用戶數據不能丟失,對於數據庫設計,如果發生變更(如多添加一張表,表的字段增加或減少等),那麼我們必須想好數據庫的更新策略。

1,定義數據庫版本

數據庫的版本是一個整型值,在創建SQLiteOpenHelper時,會傳入該數據庫的版本,如果傳入的數據庫版本號比數據庫文件中存儲的版本號大的話,那麼SQLiteOpenHelper#onUpgrade()方法就會被調用,我們的升級應該在該方法中完成。

2,如何寫升級邏輯

假如我們開發的程序已經發布了兩個版本:V1.0,V1.2,我們正在開發V1.3。每一版的數據庫版本號分別是18,19,20。

對於這種情況,我們應該如何實現升級?

用戶的選擇有:                   

1) V1.0 -> V1.3  DB 18 -> 20                  

2) V1.1 -> V1.3  DB 19 -> 20      

3,注意

數據庫的每一個版本所代表的數據庫必須是定義好的,比如說V18的數據庫,它可能隻有兩張表TableA和TableB,如果V19要添加一張表TableC,如果V20要修改TableC,那麼每一個版本所對應的數據庫結構如下:

V18  --->  TableA, TableB

V19  --->  TableA, TableB, TableC

V20  --->  TableA, TableB, TableC (變更)

onUpgrade()方法的實現如下:

  1.        // Pattern for upgrade blocks:  
  2. //  
  3. //    if (upgradeVersion == [the DATABASE_VERSION you set] - 1){  
  4. //        .. your upgrade logic..  
  5. //        upgradeVersion = [the DATABASE_VERSION you set]  
  6. //    }  
  7.   
  8.   
  9. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)  
  10. {  
  11.     int upgradeVersion  = oldVersion;  
  12.   
  13.     if (18 == upgradeVersion) {  
  14.         // Create table C  
  15.         String sql = "CREATE TABLE ...";  
  16.         db.execSQL(sql);  
  17.         upgradeVersion = 19;  
  18.     }  
  19.   
  20.     if (20 == upgradeVersion) {  
  21.         // Modify table C  
  22.         upgradeVersion = 20;  
  23.     }  
  24.   
  25.     if (upgradeVersion != newVersion) {  
  26.         // Drop tables  
  27.         db.execSQL("DROP TABLE IF EXISTS " + tableName);  
  28.         // Create tables  
  29.         onCreate(db);  
  30.     }  
  31. }  

從上麵的代碼可以看到,我們在onUpgrade()方法中,處理了數據庫版本從18 -> 20的升級過程,這樣做的話,不論用戶從18 -> 20,還是從19 -> 20,最終程序的數據庫都能升級到V20所對應的數據庫結構。  

4,如何保證數據不丟失

這是很重要的一部分,假設要更新TableC表,我們建議的做法是:       

1) 將TableC重命名為TableC_temp

       SQL語句可以這樣寫:ALERT TABLE TableC RENAME TO TableC_temp;

2) 創建新的TableC表

3) 將數據從TableC_temp中插入到TableC表中

       SQL語句可以這樣寫:INSERT INTO TableC (Col1, Col2, Col3) SELECT (Col1, Col2, Col3) FROM TableC_temp;                

                   
經過這三步,TableC就完成了更新,同時,也保留了原來表中的數據。  

注意:

在onUpgrade()方法中,刪除表時,注意使用事務處理,使得修改能立即反應到數據庫文件中。       


SQL語句

由於Android是使用開源的SQLite3作為其數據庫,所以,我們在開發數據庫模塊時,一定要注意SQLite3支持哪些關鍵字,函數等,不是所有的關鍵字,SQLite都是支持的。

下麵列出了一些參考鏈接:

SQLite3官方文檔:https://sqlite.org/

W3CSchool網站:https://www.w3school.com.cn/sql/index.asp/

SQL語句寫得好壞能直接影響到數據庫的操作。我曾經就遇到過SQL語句影響查詢性能,更新3000條記錄,用時30移左右,但在對WHERE條件的字段加上索引後,性能提升到3~4秒。

最後更新:2017-04-03 08:26:26

  上一篇:go android查看綁定源碼
  下一篇:go Netty4詳解三:Netty架構設計