GreenDao 数据库 升级 保留之前数据

很久没写博客了,最近一直很忙,没时间整理,一些内容都保存到草稿箱了,但是比较乱,需要整理后才能发,今天抽时间挑出来一篇,全是源码,描述的内容很少(基本没有,除了代码中的一些注解),相信能用到的朋友一看就能明白的;同时也给自己做个记录,方便以后用到直接拿过去就行了。

这篇的内容主要是Android本地存储之GreenDao。因为在开发中难免会有APP版本的升级,增加或者修改内容,这时候本地有保存用到GreenDao数据库的一般升级会把之前保存的数据清空。这篇文章要解决的问题就是在APP版本升级,本地数据库表或者字段有改动的情况下,不清空之前已经保存的数据。废话不多说,下面就直接上代码了:

【注意】:还要注意一点,尽量避免数据库表名有用到数据库的关键字的,如果有用到的就比较麻烦了,需要单独处理。我这里就用到了group关键字,代码里有单独处理的方法,如果你用了其它关键字就仿照我这个来写吧,嘿嘿!你如果有更好的方法顺便告诉我一声哈!

1、新建类

package com.example.test1.db;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.example.test1.utils.LoggUtils;

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.StandardDatabase;
import org.greenrobot.greendao.internal.DaoConfig;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by WJY.
 * Date: 2020/8/4
 * Time: 13:55
 * Description:兼容旧表性质的 greenDao 数据库升级,不会造成旧表的数据丢失
 */
public class GreenDaoCompatibleUpdateHelper {

    public interface GreenDaoCompatibleUpdateCallBack {
        void onFinalSuccess();
        void onFailedLog(String errorMsg);
    }

    private static GreenDaoCompatibleUpdateCallBack callBack;

    @SuppressWarnings("all")
    public void compatibleUpdate(SQLiteDatabase sqliteDatabase, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        StandardDatabase db = new StandardDatabase(sqliteDatabase);
        if (!generateNewTablesIfNotExists_withNoExchangeData(db, daoClasses))    /** 创建之前旧表中不存在的新表 */
            return;
        if (!generateTempTables_withExchangeDataFromOldTable(db, daoClasses))    /** 创建中间表 & 把旧表的数据迁移到中间表 */
            return;
        if (!dropAllTables(db, true, daoClasses))                         /** 把旧表全部删除 */
            return;
        if (!createAllTables_withNoExchangeData(db, false, daoClasses)) /** 创建所有新表 */
            return;
        restoreData_fromTempTableToNewTable(db, daoClasses);                     /** 把中间表的数据迁移到新表 & 删除中间表 */
        if (callBack != null)
            callBack.onFinalSuccess();
        callBack = null;
    }

    @SuppressWarnings("all")
    public void compatibleUpdate(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        if (!generateNewTablesIfNotExists_withNoExchangeData(db, daoClasses))
            return;
        if (!generateTempTables_withExchangeDataFromOldTable(db, daoClasses))
            return;
        if (!dropAllTables(db, true, daoClasses))
            return;
        if (!createAllTables_withNoExchangeData(db, false, daoClasses))
            return;
        restoreData_fromTempTableToNewTable(db, daoClasses);
        if (callBack != null)
            callBack.onFinalSuccess();
        callBack = null;
    }

    public GreenDaoCompatibleUpdateHelper setCallBack(GreenDaoCompatibleUpdateCallBack callBack1) {
        callBack = callBack1;
        return this;
    }

    @SafeVarargs
    private static boolean generateNewTablesIfNotExists_withNoExchangeData(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        return reflectMethod(db, "createTable", true, daoClasses);
    }

    @SafeVarargs
    private static boolean generateTempTables_withExchangeDataFromOldTable(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        try {
            for (int i = 0; i < daoClasses.length; i++) {
                DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
                String tableName = daoConfig.tablename;
                if (tableName.equals("GROUP")){
                    //group是关键字   最好不要用作表名,但是如果吴用了,用中括号[]括起来就好了,括起来后该字段就被转化为了普通的字符串
                    //如果吴用了其他关键字   则在此处处理其他字段
                    tableName = "[GROUP]";
                }
                String tempTableName = daoConfig.tablename.concat("_TEMP");
                StringBuilder insertTableStringBuilder = new StringBuilder();
                insertTableStringBuilder.append("CREATE TEMP TABLE ").append(tempTableName);
                insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");
                LoggUtils.e("cacacaca", "daoConfig="+daoConfig.toString());
                LoggUtils.e("cacacaca", "tableName="+tableName);
                LoggUtils.e("cacacaca", "insertTableStringBuilder.toString()="+insertTableStringBuilder.toString());
                db.execSQL(insertTableStringBuilder.toString());
            }
            return true;
        } catch (Exception e) {
            if (callBack != null)
                callBack.onFailedLog("generateTempTables_withExchangeDataFromOldTable ===> " + e.toString());
        }
        return false;
    }

    @SafeVarargs
    private static boolean dropAllTables(StandardDatabase db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        return reflectMethod(db, "dropTable", ifExists, daoClasses);
    }

    @SafeVarargs
    private static boolean createAllTables_withNoExchangeData(StandardDatabase db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        return reflectMethod(db, "createTable", ifNotExists, daoClasses);
    }

    /**
     * dao class already define the sql exec method, so just invoke it
     */
    @SafeVarargs
    private static boolean reflectMethod(StandardDatabase db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        if (daoClasses.length < 1) {
            if (callBack != null)
                callBack.onFailedLog("reflectMethod ===> daoClasses.length < 1");
            return false;
        }
        try {
            for (Class cls : daoClasses) {
                Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class);
                method.invoke(null, db, isExists);
            }

            // restoreData_fromTempTableToNewTable
            // ===>
            // android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: MATTER_USER_BEAN.STATUS (code 1299)
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            if (callBack != null)
                callBack.onFailedLog("reflectMethod ===> " + e.toString());
        }
        return false;
    }

    /**
     * 把旧表的数据复制到新表,不存在的字段默认值
     *
     * @param db
     * @param daoClasses
     */
    @SafeVarargs
    private static void restoreData_fromTempTableToNewTable(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        try {
            for (int i = 0; i < daoClasses.length; i++) {
                DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
                String tableName = daoConfig.tablename;
                if (tableName.equals("GROUP")){
                    //group是关键字   最好不要用作表名,但是如果吴用了,用中括号[]括起来就好了,括起来后该字段就被转化为了普通的字符串
                    //如果吴用了其他关键字   则在此处处理其他字段
                    tableName = "[GROUP]";
                }
                String tempTableName = daoConfig.tablename.concat("_TEMP");
                // get all columns from tempTable, take careful to use the columns list
                List<String> columns = getColumns(db, tempTableName);
                ArrayList<String> properties = new ArrayList<>(columns.size());
                for (int j = 0; j < daoConfig.properties.length; j++) {
                    String columnName = daoConfig.properties[j].columnName;
                    if (columns.contains(columnName)) {
                        properties.add(columnName);
                    }
                }

                if (properties.size() > 0) {
                    final String columnSQL = "`" + TextUtils.join("`,`", properties) + "`";
                    StringBuilder insertTableStringBuilder = new StringBuilder();
                    insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
                    insertTableStringBuilder.append(columnSQL);
                    insertTableStringBuilder.append(") SELECT ");
                    insertTableStringBuilder.append(columnSQL);
                    insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
                    db.execSQL(insertTableStringBuilder.toString());
                }
                StringBuilder dropTableStringBuilder = new StringBuilder();
                dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
                db.execSQL(dropTableStringBuilder.toString());
            }
        } catch (Exception e) {
            if (callBack != null)
                callBack.onFailedLog("restoreData_fromTempTableToNewTable ===> " + e.toString());
        }
    }


    private static List<String> getColumns(StandardDatabase 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) {
            if (callBack != null)
                callBack.onFailedLog("getColumns ===> " + e.toString());
        } finally {
            if (cursor != null)
                cursor.close();
            if (null == columns)
                columns = new ArrayList<>();
        }
        return columns;
    }
}

2、新建MyGreenDaoDbHelper

package com.example.test1.db;

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

import com.example.test1.db.gen.DaoMaster;
import com.example.test1.db.gen.GroupDao;
import com.example.test1.db.gen.StudentDao;
import com.example.test1.utils.LoggUtils;

import org.greenrobot.greendao.database.Database;

/**
 * Created by WJY.
 * Date: 2020/8/4
 * Time: 14:00
 * Description:
 */
public class MyGreenDaoDbHelper extends DaoMaster.DevOpenHelper {

    public MyGreenDaoDbHelper(Context context, String name) {
        super(context, name);
    }

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


    @Override
    @SuppressWarnings("all")
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        super.onUpgrade(db, oldVersion, newVersion);
        LoggUtils.e("MyGreenDaoDbHelper", "----" + oldVersion + "---先前和更新之后的版本---" + newVersion + "----");
        if (oldVersion < newVersion) {
            LoggUtils.e("MyGreenDaoDbHelper", "进行数据库升级");
            new GreenDaoCompatibleUpdateHelper()
                    .setCallBack(
                            new GreenDaoCompatibleUpdateHelper.GreenDaoCompatibleUpdateCallBack() {

                                @Override
                                public void onFinalSuccess() {
                                    LoggUtils.e("MyGreenDaoDbHelper", "进行数据库升级 ===> 成功");
                                }

                                @Override
                                public void onFailedLog(String errorMsg) {
                                    LoggUtils.e("MyGreenDaoDbHelper", "升级失败日志 ===> " + errorMsg);
                                }
                            }
                    )
                    .compatibleUpdate(
                            db,
                            StudentDao.class, GroupDao.class);
            LoggUtils.e("MyGreenDaoDbHelper", "进行数据库升级--完成");
        }
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        // 不要调用父类的,它默认是先删除全部表再创建
        // super.onUpgrade(db, oldVersion, newVersion);
    }
}

3、在MyApplication里初始化

package com.example.test1;

import android.app.Application;
import android.content.Context;

import com.example.test1.db.MyGreenDaoDbHelper;
import com.example.test1.db.gen.DaoMaster;
import com.example.test1.db.gen.DaoSession;

import org.greenrobot.greendao.identityscope.IdentityScopeType;

/**
 * Created by WJY.
 * Date: 2020/8/4
 * Time: 19:34
 * Description:
 */
public class MyApplication extends Application {

    private static MyApplication instance;
    private static Context context;
    private static DaoSession mDaoSession;

    //单例模式中获取唯一的MyApplication实例
    public static MyApplication getInstance() {
        if (null == instance) {
            instance = new MyApplication();
        }
        return instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
        setupDatabase();
    }

    /**
     * 配置数据库
     */
    private void setupDatabase() {
        //创建数据库shop.db
        MyGreenDaoDbHelper helper = new MyGreenDaoDbHelper(this, "checkterminal.db", null);
//        //获取可写数据库
//        SQLiteDatabase db = helper.getWritableDatabase();
        //获取数据库对象
        DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
        //获取dao对象管理者
        mDaoSession = daoMaster.newSession(IdentityScopeType.None);

    }

    public static DaoSession getDaoInstant() {
        return mDaoSession;
    }
}

别忘了在app的build.gradle里升级数据库版本,每次数据库有改动这里都要加1。

greendao{
    schemaVersion 2 // 版本号+1
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时代新人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值