Android 數據庫升級中數據保持和導入已有數據庫(轉)

 

一.數據庫升級:html

  在咱們的程序中,或多或少都會涉及到數據庫,使用數據庫一定會涉及到數據庫的升級,數據庫升級帶來的一些問題,如舊版本數據庫的數據記錄的保持,對新表的字段的添加等等一系列問題,還記得當我來西安的時候,面試的第二家公司,作音樂播放客戶端的,就問到了這個問題;android

  咱們開發了一個程序,當前是1.0版本。該程序用到了數據庫。到1.1版本時,在數據庫的某個表中增長了一個字段。那麼軟件1.0版本用的數據庫在軟件1.1版本就要被升級了。軟件的1.0版本升級到1.1版本時,老的數據不能丟。那麼在1.1版本的程序中就要有地方可以檢測出來新的軟件版本與老的數據庫不兼容,而且把1.0軟件的數據庫升級到1.1軟件可以使用的數據庫。也就是說,要在1.0軟件的數據庫的那個表中增長那個字段,並賦予這個字段默認值。面試

  程序如何知道咱們的數據庫須要升級呢?SQLiteOpenHelper類的構造函數有一個參數是version即數據庫版本號。好比在軟件1.0版本中,咱們使用SQLiteOpenHelper訪問數據庫時,該參數爲1,那麼數據庫版本號1就會寫在咱們的數據庫中。到了1.1版本,咱們的數據庫須要發生變化,那麼咱們1.1版本的程序中就要使用一個大於1的整數來構造SQLiteOpenHelper類,用於訪問新的數據庫,好比2。當咱們的1.1新程序讀取1.0版本的老數據庫時,就發現老數據庫裏存儲的數據庫版本是1,而咱們新程序訪問它時填的版本號爲2,系統就知道數據庫須要升級。sql

  當系統在構造SQLiteOpenHelper類的對象時,若是發現版本號不同,就會自動調用onUpgrade函數,在這個方法裏對數據庫進行升級。在這個函數中把老版本數據庫的相應表中增長字段,並給每條記錄增長默認值便可。新版本號和老版本號都會做爲onUpgrade函數的參數傳進來,便於開發者知道數據庫應該從哪一個版本升級到哪一個版本。升級完成後,數據庫會自動存儲最新的版本號爲當前數據庫版本號。數據庫

  SQLite提供了ALTER TABLE命令,容許用戶重命名或添加新的字段到已有表中,可是不能從表中刪除字段。而且只能在表的末尾添加字段,好比,爲Test添加一個字段:"ALTER TABLE Test ADDCOLUMN age"
下面是個人一個測試:ide

 

public class DataSQL extends SQLiteOpenHelper {
    public DataSQL(Context context, int version) {
        super(context, "Test", null, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table test(id integer primary key autoincrement, name)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (newVersion == 2) {
            db.execSQL("ALTER TABLE test ADD COLUMN age");
            Cursor cr = db.rawQuery("select * from test", null);
            while (cr.moveToNext()) {
                String name = cr.getString(cr.getColumnIndex("name"));
                ContentValues values = new ContentValues();
                values.put("name", name);
                values.put("age", 23);
                db.update("test", values, "name=?", new String[] { name });
            }
        }
    }
}

 

添加數據及讀取數據函數

DataSQL sql = new DataSQL(this, 2);
        SQLiteDatabase db = sql.getWritableDatabase();
        Cursor cr = db.query("test", null, null, null, null, null, null);
        while (cr.moveToNext()) {
            Log.e("cr-name", "" + cr.getString(cr.getColumnIndex("name")));
            Log.e("cr-age", "" + cr.getInt(cr.getColumnIndex("age")));
        }

 

若是遇到複雜的修改操做,好比在修改的同時,須要進行數據的轉移,那麼能夠採起在一個事務中執行以下語句來實現修改表的需求。
  1. 將表名改成臨時表
         ALTERTABLE Test RENAME TO _Test;
  2. 建立新表
        CREATETABLE Test(id VARCHAR(32PRIMARYKEY ,UserName VARCHAR(32NOTNULL , Age VARCHAR(16NOTNULL);
      3. 導入數據  
         INSERTINTO Test SELECT id, 「」, Age FROM _Test;
  或者  
        INSERTINTO Test() SELECT id, 「」, Age FROM _Test;
  * 注意 雙引號」」 是用來補充原來不存在的數據的!
      4. 刪除臨時表  
        DROPTABLE _Test;
  經過以上四個步驟,就能夠完成舊數據庫結構向新數據庫結構的遷移,而且其中還能夠保證數據不會由於升級而流失。
固然,若是遇到減小字段的狀況,也能夠經過建立臨時表的方式來實現。測試

下面仍然經過一個例子來進行測試:this

  1.修改DataSQL的onUpgrade方法spa

if (newVersion == 3) {
            char str = '"';
            db.beginTransaction();
            db.execSQL("ALTER TABLE test RENAME TO _Test");
            db.execSQL("CREATE TABLE test(id integer primary key autoincrement , PassWord VARCHAR(20) NOT NULL,"
                    + " UserName VARCHAR(32) NOT NULL , Age VARCHAR(16) NOT NULL)");
            db.execSQL("INSERT INTO test SELECT id, " + str + str
                    + ", name, age FROM _Test");
            db.setTransactionSuccessful();
            db.endTransaction();
        }

 

2.修改Activity中打印信息

            Log.e("cr-name", "" + cr.getString(cr.getColumnIndex("UserName")));
            Log.e("cr-age", "" + cr.getInt(cr.getColumnIndex("Age")));
            Log.e("cr-password", "" + cr.getInt(cr.getColumnIndex("PassWord")));

 

在實際開發工做中,咱們的處理可能比上面所述的複雜;

  假如咱們開發的程序已經發布了兩個版本:V1.0,V1.2,咱們正在開發V1.3。每一版的數據庫版本號分別是8,9,10。對於這種狀況,咱們應該如何實現升級?
用戶的選擇有:                   

  1) V1.0 -> V1.3  DB 8 -> 10                  
  2) V1.2 -> V1.3  DB 9 -> 10      
  3)注意:數據庫的每個版本所表明的數據庫必須是定義好的,好比說V1.0的數據庫,它可能只有兩張表TableA和TableB,若是V1.2要添加一張表TableC,若是V1.3要修改TableC,那麼每個版本所對應的數據庫結構以下:
V1.0  --->  TableA, TableB
V1.2  --->  TableA, TableB, TableC
V1.3  --->  TableA, TableB, TableC (Modify)

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
    {
        int upgradeVersion  = oldVersion;
        if (8 == upgradeVersion) {
            // Create table C
            String sql = "CREATE TABLE ...";
            db.execSQL(sql);
            upgradeVersion = 9;
        }
        if (9 == upgradeVersion) {
            // Modify table C
            upgradeVersion = 10;
        }
        if (upgradeVersion != newVersion) {
            // Drop tables
            db.execSQL("DROP TABLE IF EXISTS " + tableName);
            // Create tables
            onCreate(db);
        }
    }

 

在onUpgrade()方法中,處理了數據庫版本從8 -> 10的升級過程,這樣作的話,不論用戶從8 -> 10,仍是從9 - 10,最終程序的數據庫都能升級到V1.3所對應的數據庫結構。

二.導入已有數據庫

  在有的狀況下,咱們要在程序一開始運行的時候就導入某些固定的數據,而這些數據過大,又不可能直接代碼寫死,此時就須要經過導入已有數據庫的方法導入數據,咱們知道raw文件夾下的東西,android會原封不動的拷貝到程序中,而不會轉換爲二進制文件,因此,咱們把數據庫放到raw文件夾下供程序導入使用;

 

public class DBImporter {
    public static final String PACKAGE_NAME = "com.example.sql";
    public static final String DB_NAME = "xxx.db";
    public static String DB_PATH = "/data/data/" + PACKAGE_NAME;
    private Context context;

    public DBImporter(Context mContext) {
        this.context = mContext;
    }

    public SQLiteDatabase openDataBase() {
        return SQLiteDatabase.openOrCreateDatabase(DB_PATH + "/" + DB_NAME, null);
    }

    public void copyDB() {
        File file = new File(DB_PATH + "/" + DB_NAME);
        if (!file.exists()) {
            try {
                FileOutputStream out = new FileOutputStream(file);
                int buffer = 400000;
          // 讀取數據庫並保存到data/data/packagename/xx.db...
                InputStream ins = context.getResources().openRawResource(R.raw.sql_);
                byte[] bts = new byte[buffer];
                int length;
                while ((length = ins.read(bts)) > 0) {
                    out.write(bts, 0, bts.length);
                }
                out.close();
                ins.close();
                SQLiteDatabase.openOrCreateDatabase(DB_PATH + "/" + DB_NAME, null);
            } catch (FileNotFoundException e) {
            } catch (IOException e) {
            }
        }
    }
}

 

接下來即是在須要使用到該數據庫的地方調用

 

        DBImporter importer = new DBImporter(this);
        importer.copyDB();
        SQLiteDatabase db = importer.openDataBase(); // 獲取到數據庫對象,接下來即可操做了~

 

 

 

 

轉自: http://www.cnblogs.com/a284628487/p/3345820.html

相關文章
相關標籤/搜索