Android數據庫升級 數據庫升級工具分享

安卓數據庫爲SQLite輕量級數據庫,是遵守ACID的關係型數據庫管理系統,它包含在一個相對小的C庫中。它的設計目標是嵌入式的,佔用資源非常的低,在嵌入式設備中,可能只需要幾百K的內存就夠了。
在安卓SDK中,會有SQLiteOpenHelper類提供SQLite的基本操作。
在開發中,我們會重新定義SQLiteOpenHelper,並重寫onCreate和onUpgrade方法來處理數據庫表的創建和更新操作。
對於數據庫的更新,我們還是要在onUpgrade方法中去自己處理。而對於數據庫表添加新的字段或者修改字段類型,SQLite並沒有提供方法。那麼我們就只能在數據庫需要升級的時候,創建新表、複製舊數據到新表中來進行升級。


簡單介紹一下升級步驟:
1.將現有的所有表重命名,創建臨時表;
2.使用新的創建表SQL創建新表;
3.講臨時表的數據寫入新表,並刪除臨時表。

一、表對象繼承BaseTable.java

/**
 * Created by WangFeng on 2017/4/12 0012 11:38.
 *
 * @desc    數據表的基類,所有表都需要繼承此抽象類
 */
public abstract class BaseTable {

    /**
     * 獲取表名
     * @return
     */
    public abstract String getTableName();

    /**
     * 獲取創建表語句
     * @return
     */
    public abstract String getCreateSQL();

    /**
     * 獲取刪除表語句
     * @return
     */
    public abstract String getDropSQL();

}
/**
 * Created by WangFeng on 2017/3/29 0029.
 * 圖片數據表
 */
public class ImageTable extends BaseTable{

    static final String TABLE_NAME = "images";

    static final String _ID = "id";
    static final String IMAGE_URL = "image_url";
    static final String IMAGE_NAME = "image_name";
    static final String IMAGE_DESC = "image_desc";
    static final String CREATE_TIME = "create_time";
    static final String UPDATE_TIME = "update_time";
    static final String REMOVED = "removed";

    static final String SQL_CREATE = new StringBuilder()
            .append("CREATE TABLE ").append(TABLE_NAME)
            .append("(")
            .append(_ID).append(" VARCHAR(36) PRIMARY KEY NOT NULL,")
            .append(IMAGE_URL).append(" VARCHAR(100),")
            .append(IMAGE_NAME).append(" VARCHAR(16),")
            .append(IMAGE_DESC).append(" TEXT,")
            .append(CREATE_TIME).append(" CHAR(20),")
            .append(UPDATE_TIME).append(" CHAR(20),")
            .append(REMOVED).append(" BOOLEAN DEFAULT FALSE")
            .append(")")
            .toString();

    static final String SQL_DROP = new StringBuilder()
            .append("DROP TABLE ").append(TABLE_NAME)
            .toString();

    @Override
    public String getTableName() {
        return TABLE_NAME;
    }

    @Override
    public String getCreateSQL() {
        return SQL_CREATE;
    }

    @Override
    public String getDropSQL() {
        return SQL_DROP;
    }
}

二、導入DBUpgradeHelper.java

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.support.annotation.NonNull;
import android.text.TextUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by WangFeng on 2017/4/12 0012 11:38.
 *
 * @desc 數據庫表的升級
 */
public class DBUpgradeHelper {

    private volatile static DBUpgradeHelper mInstance;

    private DBUpgradeHelper() {
    }

    public static DBUpgradeHelper getInstance() {
        if (mInstance == null) {
            synchronized (DBUpgradeHelper.class) {
                if (mInstance == null) {
                    mInstance = new DBUpgradeHelper();
                }
            }
        }
        return mInstance;
    }

    /**
     * 升級數據庫表
     *
     * @param db     數據庫
     * @param tables 要升級的表
     */
    public void upgrade(SQLiteDatabase db, BaseTable... tables) {
        createTempTables(db, tables); // 創建臨時表
        dropTables(db, tables); // 刪除舊錶
        createNewTables(db, tables); // 創建新表
        restoreData(db, tables); // 臨時表數據寫入新表,刪除臨時表
    }


    /**
     * 創建臨時表,存儲舊的表數據
     *
     * @param db
     * @param tables
     */
    private void createTempTables(SQLiteDatabase db, BaseTable... tables) {
        for (int i = 0; i < tables.length; i++) {
            String tableName = tables[i].getTableName();
            if (!checkTable(db, tableName))
                continue;
            String tempTableName = tableName.concat("_TEMP");
            String SQL = new StringBuilder()
                    .append("ALTER TABLE ")
                    .append(tableName)
                    .append(" RENAME TO ")
                    .append(tempTableName)
                    .toString();
            db.execSQL(SQL);
        }
    }

    /**
     * 檢查表是否存在
     *
     * @param db
     * @param tableName
     * @return
     */
    private Boolean checkTable(SQLiteDatabase db, String tableName) {
        String SQL = new StringBuilder()
                .append("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='")
                .append(tableName)
                .append("'")
                .toString();
        Cursor c = db.rawQuery(SQL, null);
        if (c.moveToNext()) {
            int count = c.getInt(0);
            if (count > 0) {
                return true;
            }
            return false;
        }
        return false;
    }

    /**
     * 刪除舊錶
     *
     * @param db
     * @param tables
     */
    private void dropTables(SQLiteDatabase db, @NonNull BaseTable... tables) {
        for (int i = 0; i < tables.length; i++) {
            if(!checkTable(db, tables[i].getTableName()))
                continue;
            try {
                db.execSQL(tables[i].getDropSQL());
            } catch (SQLiteException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 創建新的表結構
     *
     * @param db
     * @param tables
     */
    private void createNewTables(SQLiteDatabase db, @NonNull BaseTable... tables) {
        for (int i = 0; i < tables.length; i++) {
            if(checkTable(db, tables[i].getTableName()))
                continue;
            try {
                db.execSQL(tables[i].getCreateSQL());
            } catch (SQLiteException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 將臨時表的數據寫入新表
     *
     * @param db
     * @param tables
     */
    private void restoreData(SQLiteDatabase db, BaseTable... tables) {
        for (int i = 0; i < tables.length; i++) {
            String tableName = tables[i].getTableName();
            String tempTableName = tableName.concat("_TEMP");
            if (!checkTable(db, tempTableName))
                continue;
            List<String> columns_temp = getColumns(db, tempTableName);
            List<String> columns_new = getColumns(db, tempTableName);
            ArrayList<String> properties = new ArrayList<>(columns_temp.size());
            for (int j = 0; j < columns_new.size(); j++) {
                String columnName = columns_new.get(j);
                if (columns_temp.contains(columnName)) {
                    properties.add(columnName);
                }
            }
            // 從臨時表複製表數據到相同的字段
            if (properties.size() > 0) {
                String columnSQL = TextUtils.join(",", properties);

                String SQL = new StringBuilder()
                        .append("INSERT INTO ").append(tableName)
                        .append(" (").append(columnSQL).append(") SELECT ").append(columnSQL)
                        .append(" FROM ").append(tempTableName)
                        .toString();
                try {
                    db.execSQL(SQL);
                } catch (SQLiteException e) {
                    e.printStackTrace();
                }
            }
            // 刪除臨時表
            String SQL_DROP = new StringBuilder()
                    .append("DROP TABLE ").append(tempTableName)
                    .toString();
            try {
                db.execSQL(SQL_DROP);
            } catch (SQLiteException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 獲取表字段列表
     *
     * @param db
     * @param tableName
     * @return
     */
    private List<String> getColumns(SQLiteDatabase db, String tableName) {
        List<String> columns = null;
        Cursor cursor = null;
        try {
            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null);
            if (null != cursor && cursor.getColumnCount() > 0) {
                columns = Arrays.asList(cursor.getColumnNames());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null)
                cursor.close();
            if (null == columns)
                columns = new ArrayList<>();
        }
        return columns;
    }

}

三、構建SQLiteOpenHelper,進行創建和升級數據庫

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * Created by WangFeng on 2017/3/27 0027.
 * <p>
 * 本地數據庫
 */
public class OfflineDB {

    public static final String DB_NAME = "myDB";
    public static final int DB_VER = 4; // 數據庫版本號,每次更改表結構或添加新表時,需要將版本號++

    private SQLiteDatabase mDB;
    private MyDBHelper mHelper;

    public OfflineDB(Context context) {
        mHelper = new MyDBHelper(context);
        mDB = mHelper.getReadableDatabase();
        mDB = mHelper.getWritableDatabase();
    }

    public SQLiteDatabase getDB() {
        return mDB;
    }

    class MyDBHelper extends SQLiteOpenHelper {

        public MyDBHelper(Context context) {
            super(context, DB_NAME, null, DB_VER);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            // 創建新數據庫時調用,進行數據表創建
            db.execSQL(ImageTable.SQL_CREATE);
            db.execSQL(VideoTable.SQL_CREATE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // 數據庫版本改變時調用,執行數據庫表升級,將需要升級的表傳入upgrade方法
            DBUpgradeHelper.getInstance().upgrade(db, new ImageTable(), new VideoTable());
        }

    }

}

簡單的數據庫升級方法就介紹完了。
本人經驗尚淺,若有不妥之處,還望大神多多指教,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章