GreenDao深入使用——升級不刪除老數(shù)據(jù)

剛開始用的時候知道Gradle里用配置數(shù)據(jù)庫版本的參數(shù),即:

greendao {
    schemaVersion 1
    daoPackage 'com.tao.greendao.gen'
    targetGenDir 'src/main/java'
}

開始以為升級還不容易么泣特,不就是把schemaVersion 抬高一個版本不就ok了嗎。但有實際需求的時候就不是這么回事了宅粥。
把schemaVersion 變成2的時候帮碰,一運行,再查看數(shù)據(jù)庫尼酿。要加的字段確實添加進來了爷狈。但是以前保存的數(shù)據(jù)都沒有了。這樣在保留數(shù)據(jù)升級的場景中就不適用了裳擎。我們要想不刪除涎永,首先要了解下GreenDao是怎么做的,那就從代碼入手吧鹿响。
DaoMaster中的代碼:

 /** WARNING: Drops all table on Upgrade! Use only during development. */
    public static class DevOpenHelper extends OpenHelper {
        public DevOpenHelper(Context context, String name) {
            super(context, name);
        }

        public DevOpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory);
        }

        @Override
        public void onUpgrade(Database db, int oldVersion, int newVersion) {
            Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
            dropAllTables(db, true);
            onCreate(db);
        }
    }

人家早就說啦Use only during development.羡微,都怪自己沒看代碼。代碼中寫的很明確惶我,升級的時候是刪除所有表妈倔,然后再重新創(chuàng)建數(shù)據(jù)庫。
我們應(yīng)該每次提高數(shù)據(jù)庫版本的時候在表中插入新的字段不就行了嗎绸贡,于是出現(xiàn)了如下代碼:

 @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion < 2) {
            db.execSQL(String.format("ALTER TABLE %s ADD %s varchar", 表名, 字段名))启涯;
        }
        if (oldVersion < 3) {
            db.execSQL(String.format("ALTER TABLE %s ADD %s integer default 0", 表名,字段名));
        }
}

項目上線后,隨著數(shù)據(jù)庫的不斷升級恃轩,onUpgrade中的代碼越來越多。時間久了會吐了的黎做。所以要換一種寫法叉跛,大體思路:
1.首先創(chuàng)建臨時表。
2.把當前表的數(shù)據(jù)插入到臨時表中去蒸殿。
3.刪除掉原表筷厘,創(chuàng)建新表。
4.把臨時表數(shù)據(jù)插入到新表中去宏所,然后刪除臨時表酥艳。
感覺還是很復(fù)雜,就在網(wǎng)上找了下爬骤,有人已經(jīng)寫好了:

/**
 * 作者:WangJintao
 * 時間:2017/4/17
 * 郵箱:wangjintao1988@163.com
 */

public class MySqlLiteOpenHelper extends DaoMaster.DevOpenHelper {
    public MySqlLiteOpenHelper(Context context, String name) {
        super(context, name);
    }

    public MySqlLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
        super(context, name, factory);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        MigrationHelper.getInstance().migrate(db, StudentDao.class);
    }
}
/**
 * 作者:WangJintao
 * 時間:2017/4/17
 * 郵箱:wangjintao1988@163.com
 */

public class MigrationHelper {
    private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
    private static MigrationHelper instance;

    public static MigrationHelper getInstance() {
        if (instance == null) {
            instance = new MigrationHelper();
        }
        return instance;
    }

    private static List<String> getColumns(Database db, String tableName) {
        List<String> columns = new ArrayList<>();
        Cursor cursor = null;
        try {
            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
            if (cursor != null) {
                columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
            }
        } catch (Exception e) {
            Log.v(tableName, e.getMessage(), e);
            e.printStackTrace();
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return columns;
    }

    public void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        generateTempTables(db, daoClasses);
        DaoMaster.dropAllTables(db, true);
        DaoMaster.createAllTables(db, false);
        restoreData(db, daoClasses);
    }

    private void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);

            String divider = "";
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            ArrayList<String> properties = new ArrayList<>();

            StringBuilder createTableStringBuilder = new StringBuilder();

            createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");

            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;

                if (getColumns(db, tableName).contains(columnName)) {
                    properties.add(columnName);

                    String type = null;

                    try {
                        type = getTypeByClass(daoConfig.properties[j].type);
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    }

                    createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);

                    if (daoConfig.properties[j].primaryKey) {
                        createTableStringBuilder.append(" PRIMARY KEY");
                    }

                    divider = ",";
                }
            }
            createTableStringBuilder.append(");");
            Log.i("lxq", "創(chuàng)建臨時表的SQL語句: " + createTableStringBuilder.toString());
            db.execSQL(createTableStringBuilder.toString());

            StringBuilder insertTableStringBuilder = new StringBuilder();

            insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(" FROM ").append(tableName).append(";");
            Log.i("lxq", "在臨時表插入數(shù)據(jù)的SQL語句:" + insertTableStringBuilder.toString());
            db.execSQL(insertTableStringBuilder.toString());
        }
    }

    private void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);

            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            ArrayList<String> properties = new ArrayList();
            ArrayList<String> propertiesQuery = new ArrayList();
            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;

                if (getColumns(db, tempTableName).contains(columnName)) {
                    properties.add(columnName);
                    propertiesQuery.add(columnName);
                } else {
                    try {
                        if (getTypeByClass(daoConfig.properties[j].type).equals("INTEGER")) {
                            propertiesQuery.add("0 as " + columnName);
                            properties.add(columnName);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            StringBuilder insertTableStringBuilder = new StringBuilder();

            insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", propertiesQuery));
            insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");

            StringBuilder dropTableStringBuilder = new StringBuilder();

            dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
            Log.i("lxq", "插入正式表的SQL語句:" + insertTableStringBuilder.toString());
            Log.i("lxq", "銷毀臨時表的SQL語句:" + dropTableStringBuilder.toString());
            db.execSQL(insertTableStringBuilder.toString());
            db.execSQL(dropTableStringBuilder.toString());
        }
    }

    private String getTypeByClass(Class<?> type) throws Exception {
        if (type.equals(String.class)) {
            return "TEXT";
        }
        if (type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class) || type.equals(int.class)) {
            return "INTEGER";
        }
        if (type.equals(Boolean.class) || type.equals(boolean.class)) {
            return "BOOLEAN";
        }

        Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
        exception.printStackTrace();
        throw exception;
    }
}
MigrationHelper.getInstance().migrate(db, StudentDao.class);

中可以把所有的XXXDao.class都傳進去充石。
修改后的DaoManager變成這樣:

/**
 * 作者:WangJintao
 * 時間:2017/4/17
 * 郵箱:wangjintao1988@163.com
 */

public class GreenDaoManager {
    private static DaoMaster mDaoMaster;
    private static DaoSession mDaoSession;
    private static volatile GreenDaoManager mInstance = null;
    private static final String DB_NAME = "GreenDao.db";

    private GreenDaoManager() {
    }

    public static void init(Context context) {
        MySqlLiteOpenHelper devOpenHelper = new
                MySqlLiteOpenHelper(context, DB_NAME);
        mDaoMaster = new DaoMaster(devOpenHelper.getWritableDatabase());
        mDaoSession = mDaoMaster.newSession();
    }

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

    public DaoSession getSession() {
        return mDaoSession;
    }
}

在Application中初始化:

GreenDaoManager.init(this);

還有個問題沒搞出來,希望哪位會的指點一下
某個Entity中有個字段是List<String>該怎么存儲呢

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霞玄,一起剝皮案震驚了整個濱河市骤铃,隨后出現(xiàn)的幾起案子拉岁,更是在濱河造成了極大的恐慌,老刑警劉巖惰爬,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喊暖,死亡現(xiàn)場離奇詭異,居然都是意外死亡撕瞧,警方通過查閱死者的電腦和手機陵叽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丛版,“玉大人巩掺,你說我怎么就攤上這事∨鹦觯” “怎么了锌半?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寇漫。 經(jīng)常有香客問我刊殉,道長,這世上最難降的妖魔是什么州胳? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任记焊,我火速辦了婚禮,結(jié)果婚禮上栓撞,老公的妹妹穿的比我還像新娘遍膜。我一直安慰自己,他們只是感情好瓤湘,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布瓢颅。 她就那樣靜靜地躺著,像睡著了一般弛说。 火紅的嫁衣襯著肌膚如雪挽懦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天木人,我揣著相機與錄音信柿,去河邊找鬼。 笑死醒第,一個胖子當著我的面吹牛渔嚷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播稠曼,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼形病,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起窒朋,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤搀罢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后侥猩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榔至,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年欺劳,在試婚紗的時候發(fā)現(xiàn)自己被綠了唧取。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡划提,死狀恐怖枫弟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鹏往,我是刑警寧澤淡诗,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站伊履,受9級特大地震影響韩容,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唐瀑,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一群凶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哄辣,春花似錦请梢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至当窗,卻和暖如春形真,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背超全。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留邓馒,地道東北人嘶朱。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像光酣,于是被迫代替她去往敵國和親疏遏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容