GreenDao 兼容升级,保留旧数据的---全方面解决方案
作者:林冠宏 / 指尖下的幽灵
GitHub : https://github.com/af913337456/
腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities
GreenDaoCompatibleUpdateHelper
一个能帮助你成功升级基于 greenDao 创建的数据库而不会丢失之前数据的工具
A helper which can help you to update you database without lost your old datas, simple to use , enjoy.
开源地址
https://github.com/af913337456/LghGreenDaoCompatibleUpdateHelper
目录
- 出问题的的情形
- 几个事实
- 解决方案
- 代码简述
- 产品级别的可能错误
- 你的顾虑
出问题的的情形:
- 字段添加,导致旧表格字段与新的不匹配引发
android.database.sqlite.SQLiteException类异常。 - 服务端数据返回无法与就表格匹配,无法进行插入操作
第一个情况会直接导致 APP 闪退掉,第二种就是数据不匹配。
几个事实
- GreenDao 目前的 3.+ 版,自动生成的代码的升级方式都是
先删除原来的表格,再创建新的
/** WARNING: Drops all table on Upgrade! Use only during development. */
public static class DevOpenHelper extends OpenHelper {
......
@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);
}
}
- 凡是自动生成的代码文件,例如
xxxDao.java类的,都会在每一次 build 的时候重新被生成,意味着个人的内嵌修改总是无效,因为总是覆盖你的。 - 数据库的升级方式需求更多是需要往后兼容的,旧数据不能丢失
解决方案
自定义升级策略。
思路参考
在上面的基础上做出如下步骤总结: (看不懂的看下面的符号描述)
- 创建之前旧表中不存在的新表
- 创建中间表 & 把旧表的数据迁移到中间表
- 把旧表全部删除
- 创建所有新表
- 把中间表的数据迁移到新表 & 删除中间表
对应上面的步骤描述:
- A -> A + B , old: A , new: B
- use (A+B) -> create temp (A'+B') & insert data
- drop (A+B) , contain old datas
- create (A+B) , abs empty tables
- restore data to (A+B) from (A'+B') then drop (A'+B')
代码简述
基于上面的二次修改和拓展
GreenDaoCompatibleUpdateHelper.java顾名思义,兼容旧表性质的 greenDao 数据库升级,不会造成旧表的数据丢失- 拓展了最终的成功和失败的回调
- 添加了错误日志的处理
- 解决了字段名称的冲突 bug,例如 delete 之类
MyGreenDaoDbHelper.java自定义的 dbHelper,重载 onUpgrade
调用例子
if (oldVersion < newVersion) {
Log.e("MyGreenDaoDbHelper","进行数据库升级");
new GreenDaoCompatibleUpdateHelper()
.setCallBack(
new GreenDaoCompatibleUpdateHelper.GreenDaoCompatibleUpdateCallBack() {
@Override
public void onFinalSuccess() {
Log.e("MyGreenDaoDbHelper","进行数据库升级 ===> 成功");
}
@Override
public void onFailedLog(String errorMsg) {
Log.e("MyGreenDaoDbHelper","升级失败日志 ===> "+errorMsg);
}
}
)
.compatibleUpdate(
db,
PostBeanDao.class,
MatterUserBeanDao.class,
PropsBeanDao.class,
ChannelChatsBeanDao.class,
JoinToChannelReqBeanDao.class
);
Log.e("MyGreenDaoDbHelper","进行数据库升级--完成");
}
GreenDaoCompatibleUpdateHelper
public final 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;
}
public GreenDaoCompatibleUpdateHelper setCallBack(GreenDaoCompatibleUpdateCallBack callBack1){
callBack = callBack1;
return this;
}
...... // 去 gitHub 下载完整代码
}
MyGreenDaoDbHelper
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);
Log.e("MyGreenDaoDbHelper", "----"+oldVersion + "---先前和更新之后的版本---" + newVersion+"----");
if (oldVersion < newVersion) {
Log.e("MyGreenDaoDbHelper","进行数据库升级");
new GreenDaoCompatibleUpdateHelper()
.setCallBack(
new GreenDaoCompatibleUpdateHelper.GreenDaoCompatibleUpdateCallBack() {
@Override
public void onFinalSuccess() {
Log.e("MyGreenDaoDbHelper","进行数据库升级 ===> 成功");
}
@Override
public void onFailedLog(String errorMsg) {
Log.e("MyGreenDaoDbHelper","升级失败日志 ===> "+errorMsg);
}
}
)
.compatibleUpdate(
db,
PostBeanDao.class,
MatterUserBeanDao.class,
PropsBeanDao.class,
ChannelChatsBeanDao.class,
JoinToChannelReqBeanDao.class
);
Log.e("MyGreenDaoDbHelper","进行数据库升级--完成");
}
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
// 不要调用父类的,它默认是先删除全部表再创建
// super.onUpgrade(db, oldVersion, newVersion);
}
}
产品级别的可能错误
- 因为混淆了 dao 类文件,导致 createTable 方法找不到,解决方法,不要混淆 dao 文件
- restore 步骤中因为新加入的字段含有 int boolean 基础类型,因为不具备默认值而导致出现
SQLiteConstraintException: NOT NULL constraint failed错误,解决方法,采用 Integer Boolean 类型替换,这个你只能妥协,因为 greenDao 作者不屑于在你建表的时候提供默认值方法。详情可以去看看 issue
你的顾虑
- 如果我的表太多了,升级会不会造成 ANR 或者导致读写混乱?
- 是否实践过?
1, 答: sqlLite 的源码里面调用 onUpdrade方法的入口皆加上了同步琐,这样不会造成在升级中还能让你去读写的情况。
这点设计得非常优秀!表太多的,几百张?那么就放入子线程升级。
2, 答: 我已经使用到线上多个APP , 且成功运行至今。
GreenDao 兼容升级,保留旧数据的---全方面解决方案的更多相关文章
- Android版本升级同时Sqlite数据库的升级及之前数据的保留
http://www.cnblogs.com/wang340/archive/2013/05/06/3063135.html http://www.eoeandroid.com/forum.php?m ...
- 前端必备——js中前端与后台的数据交互全解
只要编程语言能够支持网卡端口的监听和发送,理论上都是可以实现服务器后台设计的.也因此造成了实现后台的语言偏多,而web前端语言以html/css/js为主.所以在这里我们不涉及后台的设计,只介绍在we ...
- 从 vCenter Server 使用的数据库中清除旧数据 (2075138)(转)
Document Id 2075138 Symptoms 免责声明: 本文为 Purging old data from the database used by VMware vCenter Ser ...
- solr6.4.2之webservice兼容升级
摘要:这次solr底层升级是一次比较大的升级.从底层搜索引擎 solr4.8 升级到 solr6.4.2,由于solr底层从6.x开始以来的jdk必须指定为1.8,而且很多内部实现类都已经废弃或者干脆 ...
- 尽可能保留原有数据,建立UEFI与BIOS双启PE优盘
尽可能保留原有数据,建立UEFI与BIOS双启PE优盘1.确保优盘或者移动硬盘有一个FAT32分区,如果没有FAT32分区,就用傲梅分区助手或者ppm转换一个现有的分区到FAT32分区0x0C,或者新 ...
- mysql更改已有数据表的字符集,保留原有数据内容
mysql更改已有数据表的字符集,保留原有数据内容 原文网址:http://blog.csdn.net/learn_2/article/details/6460370 环境:在应用开始阶段没有 ...
- 资源下载南方cass视频教程,包括文档,数据,很全的
废话就不多说了,开始... 北方cass视频教程,包括文档,数据,很全的 视频下载地址:http://www.400gb.com/file/23459263 GIS网盘进入下载:http://laoh ...
- 导入旧数据需要 使用date插件
"@version" => "1", "@timestamp" => "2016-09-12T08:31:06.630 ...
- 【甘道夫】使用HIVE SQL实现推荐系统数据补全
需求 在推荐系统场景中,假设基础行为数据太少,或者过于稀疏,通过推荐算法计算得出的推荐结果非常可能达不到要求的数量. 比方,希望针对每一个item或user推荐20个item,可是通过计算仅仅得到8个 ...
随机推荐
- 2329: [HNOI2011]括号修复
传送魔法 一开始以为可以直接线段树的,好像还是不行……还是得用Spaly,然后就没啥了. #include<cstdio> #include<algorithm> #defin ...
- POJ3041-Asteroids-匈牙利算法
Asteroids Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 23963 Accepted: 12989 Descr ...
- Linux系统调优权威指南
1.关闭SELINUX功能1.1 修改配置文件,使关闭SELINUX永久生效sed 's#SELINUX=enforcing#SELINUX=disables#g' /etc/selinux/conf ...
- Sass嵌套
Sass 中还提供了选择器嵌套功能,但这也并不意味着你在 Sass 中的嵌套是无节制的,因为你嵌套的层级越深,编译出来的 CSS 代码的选择器层级将越深,这往往是大家不愿意看到的一点. 选择器嵌套为样 ...
- 十二个 ASP.NET Core 例子——中间件
目录: 什么是中间件(IApplicationBuilder) 创建 顺序规则 Asp.Net Core 内置的中间件 1.什么是中间件 官方:中间件是组装成应用程序管道以处理请求和响应的软件.每个组 ...
- ThinkPHP5+小程序商城 网盘视频
ThinkPHP5+小程序商城 网盘视频 有需要联系我 QQ:1844912514
- nginx服务器配置/websocket nginx 配置笔记
server { listen 80; server_name xxx.com; # add_header '*' ; location /u/ { # 反向代理透传客户端ip proxy_set_h ...
- ThinkPHP配置文件的加载
分类: thinkphp(39) 版权声明:本文为博主原创文章,未经博主允许不得转载. 惯例配置->应用配置->模式配置->调试配置->状态配置->模块配置->扩展 ...
- 使用wrk进行性能测试
1 wrk介绍 wrk是一款现代化的HTTP性能测试工具,即使运行在单核CPU上也能产生显著的压力.它融合了一种多线程设计,并使用了一些可扩展事件通知机制,例如epoll and kqueue. 一个 ...
- 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](七)
前言 大家好,我依旧是你们的老朋友Rector,很高兴又在周五的时候准时和大家见面. Rector的系列文章[一步一步创建ASP.NET MVC5程序[Repository+Autofac+Autom ...