2012-08-06

Android 手機資料庫(五) - SQLite之資料庫更新說明篇


如果你對資料庫還完全陌生,請參考之前的三篇文章(第一篇第二篇第三篇)

因為很多人都不了解Android的資料庫更新應該怎麼做,所以在此寫一篇說明文好了

希望不了解的人看完之後能比較有所了解,下面開始解釋:

先貼上資料庫的程式碼,裡面當然也包含一般更新資料庫(onUpgrade)


程式碼
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;

public class NewListDataSQL extends SQLiteOpenHelper {

  private static final int VERSION = 3;//資料庫版本  
  
  public NewListDataSQL(Context context, String name, CursorFactory factory,int version) { //建構子
   super(context, name, factory, version);
  }

  public NewListDataSQL(Context context,String name) { 
   this(context, name, null, VERSION); 
   } 
  
   public NewListDataSQL(Context context, String name, int version) {  
    this(context, name, null, version);  
      }  
   
  //輔助類建立時運行該方法
  @Override
  public void onCreate(SQLiteDatabase db) {
   String DATABASE_CREATE_TABLE =
     "create table newMemorandum("
       + "_ID INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL,"
             + "date VARCHAR,"
             + "note VARCHAR,"
             + "pw VARCHAR,"
             + "reminder INT,"
             + "type VARCHAR,"
             + "memo VARCHAR"
         + ")";
            db.execSQL(DATABASE_CREATE_TABLE);
  }

  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
   //oldVersion=舊的資料庫版本;newVersion=新的資料庫版本
     //db.execSQL("DROP TABLE IF EXISTS newMemorandum"); //刪除舊有的資料表
     //onCreate(db);
   
   
   if (newVersion > oldVersion) {
       db.beginTransaction();
    
       boolean success = true;
       
         
         switch (oldVersion) {
           case 2:           
            
            Cursor cursor = db.rawQuery("select _ID,date from newMemorandum", null);
            int[] iID = new int[cursor.getCount()];
            String[] sDate = new String[cursor.getCount()];
            
            int rows_num = cursor.getCount(); //取得資料表列數
            
            if(rows_num != 0) {
            cursor.moveToFirst();   //將指標移至第一筆資料
            for(int i=0; i<rows_num; i++) {
             int iCr = cursor.getInt(0);
             String strCr = cursor.getString(1);
             iID[i]=iCr;
             sDate[i]=strCr.split(" ")[0].split("/")[0] + "/" + String.valueOf(Integer.valueOf(strCr.split(" ")[0].split("/")[1])) + "/" + String.valueOf(Integer.valueOf(strCr.split(" ")[0].split("/")[2])) + " " + strCr.split(" ")[2];
             
             //因為日期格式有變,所以重新更新
             //格式改為2012/6/6 12:02
             ContentValues cv = new ContentValues();
             cv.put("date", sDate[i]);
             
             db.update("newMemorandum", cv, "_ID=" + iID[i], null);
             
             cv = null;//消滅物件
             
             
             cursor.moveToNext();  //將指標移至下一筆資料
            }
           }
           cursor.close();  //關閉Cursor
            
           
            db.execSQL("ALTER TABLE newMemorandum ADD COLUMN reminder integer DEFAULT 0");
            db.execSQL("ALTER TABLE newMemorandum ADD COLUMN type VARCHAR");
            db.execSQL("ALTER TABLE newMemorandum ADD COLUMN memo VARCHAR");
            oldVersion++;
            
               success = true;
             break;
           case 1:
                        
            Cursor cursor2 = db.rawQuery("select _ID,date from newMemorandum", null);
            int[] iID2 = new int[cursor2.getCount()];
            String[] sDate2 = new String[cursor2.getCount()];
            
            int rows_num2 = cursor2.getCount(); //取得資料表列數
            
            if(rows_num2 != 0) {
             cursor2.moveToFirst();   //將指標移至第一筆資料
            for(int j=0; j<rows_num2; j++) {
             int iCr2 = cursor2.getInt(0);
             String strCr = cursor2.getString(1);
             iID2[j]=iCr2;
             sDate2[j]=strCr.split(" ")[0].split("/")[0] + "/" + String.valueOf(Integer.valueOf(strCr.split(" ")[0].split("/")[1])) + "/" + String.valueOf(Integer.valueOf(strCr.split(" ")[0].split("/")[2])) + " " + strCr.split(" ")[2];
             
             //因為日期格式有變,所以重新更新
             //格式改為2012/6/6 12:02
             ContentValues cv2 = new ContentValues();
             cv2.put("date", sDate2[j]);
             
             db.update("newMemorandum", cv2, "_ID=" + iID2[j], null);
             
             cv2 = null;//消滅物件
             
             
             cursor2.moveToNext();  //將指標移至下一筆資料
            }
           }
            cursor2.close();  //關閉Cursor
           
            db.execSQL("ALTER TABLE newMemorandum ADD COLUMN pw VARCHAR");
            db.execSQL("ALTER TABLE newMemorandum ADD COLUMN reminder integer DEFAULT 0");
            db.execSQL("ALTER TABLE newMemorandum ADD COLUMN type VARCHAR");
            db.execSQL("ALTER TABLE newMemorandum ADD COLUMN memo VARCHAR");
            oldVersion = 3;
               success = true;
             break;
         }
           
    
       if (success) {
         db.setTransactionSuccessful();
       }
       db.endTransaction();
     }
     else {
       onCreate(db);
     }
   
  }
  
  @Override    
   public void onOpen(SQLiteDatabase db) {     
           super.onOpen(db);       
           // TODO 每次成功打開數據庫後首先被執行     
       } 

   @Override
        public synchronized void close() {
            super.close();
            // TODO 每次關閉數據庫後被執行     
        }
}


說明:

此為資料庫SQLiteOpenHelper類的程式碼,幾個地方分析

1.資料庫的執行方式  
當系統要new出SQLiteOpenHelper類時,會先去找應用程式的DB資料夾下是否有該資料庫
如果沒有就執行建構子及onCreate建構該類別
如果有就執行onUpgrade更新資料庫

2.private static final int VERSION = 3;//資料庫版本  代表的意義與作用
(1)如果手機中沒有該應用程式,也等於沒有該資料庫(因為資料庫是獨立個體)
就會執行建構子構建該資料庫,建構子會帶入此VERSION,也就是從第3版開始
之後的更新也是從第4版開始,不能再回到第1版開始。

(2)如果有資料庫就會跳到onUpgrade事件中
oldVersion=舊的資料庫版本
newVersion=新的資料庫版本
這兩個是怎麼出現的呢?
在要更新資料庫時,您之前的VERSION一定是寫成這樣VERSION = 2
VERSION = 2 就是  = oldVersion
程式有修改時,資料庫也有修改,這時也會把VERSION改為VERSION = 3
VERSION = 3 就是 = newVersion
當你更新資料庫時,程式判別出VERSION = 3比之前的VERSION = 2大時
就會自動跳到onUpgrade事件中,帶入oldVersion=2、newVersion=3
當然你的舊版也可能是VERSION = 1(因為沒有即時更新)
程式還是會帶入oldVersion=1、newVersion=3

3.onUpgrade資料庫更新
(1)當我們了解了程式是怎麼判別資料庫更新後,再來看資料庫是怎麼更新的
因為舊版本可能不一樣,有可能最新版是第3版,舊版有1、2兩版
所以這裡可以針對不同版本做不同的更新動作,因為各版更新的都不同
所以可以用switch case分開各版本再對各版本做不同的更新動作(如上面程式碼)

(2)更新資料庫不止可以下ALTER的SQL指令而已,因為資料本身是存在的
只要不下Drop,所以可以用Cursor cursor = db.rawQuery把資料撈出來做變更
我個人是把資料撈出來做日期格式的變更,最後再下ALTER新增欄位
注意看程式碼中1版是ALTER了4個欄位,2版是ALTER了3個欄位
所以如果你是放上google play上是要針對每個版本做不同的更新
因為你不知道下載者有沒有即時去更新你的應用程式,所以基本上要保留資料
就要這樣寫把之前的版本做不同的更新,這樣才能確保每一個版本都能更新
到最新的版本而不會出錯。

以上說明希望能幫助你對Android的資料庫有了進一步的了解,謝謝。



2 則留言:

  1. 不知道您會不會遇到比方說我版本是第8版
    然後到第9版新增一個欄位後也是可以執行
    到了第10版想說砍到整個資料庫的東西
    但是在第10版他卻會說沒有我第九版的新增欄位
    因次都會出錯!!
    所以在第十一版又重複ALTER最後一個欄位才不會出錯

    不知道有沒有遇過這種問題

    回覆刪除
    回覆
    1. 會出錯的應該是從第8版要Update的

      所以你要update第10版時應該要寫兩個版本的判斷

      如果是第8版來的就要先做新增一個欄位

      如果是第9版來的就不用

      重點在ALTER欄位當然都不會出錯,因為最多就沒有ALTER到欄位而已

      只有在寫入資料時會出錯而已,在升級資料庫版本時是不會出錯的

      不過如果要砍資料庫的東西,當然沒有那個欄位時要砍就一定會馬上出錯

      刪除

您的寶貴建議是我前進的動力!