# LghGreenDaoCompatibleUpdateHelper
**Repository Path**: chen3441251/LghGreenDaoCompatibleUpdateHelper
## Basic Information
- **Project Name**: LghGreenDaoCompatibleUpdateHelper
- **Description**: greenDao, green dao update,update,database,update helper
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-08-24
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
> 作者:林冠宏 / 指尖下的幽灵
> 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8
> 博客:http://www.cnblogs.com/linguanh/
> GitHub : https://github.com/af913337456/
> 腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities
### GreenDaoCompatibleUpdateHelper
A helper which can help you to update you database without lost your old datas, simple to use , enjoy.
### Thanks
Giant
### Principle & Optimization
Principle:
* 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')
Optimization:
* add callback interface
* add error msg log
* fix some bug & format sql , like ``insert(name)`` => ``insert(`name`)`` which may cause some conflict。
### Usage
* change the default ``DaoMaster.DevOpenHelper`` to ``MyGreenDaoDbHelper`` in the right place.
* do your job in ``MyGreenDaoDbHelper``' s ``onUpgrade`` , like below :
```java
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","进行数据库升级--完成");
}
```
### Chinese Introduction
# 目录
* 出问题的的情形
* 几个事实
* 解决方案
* 代码简述
* 产品级别的可能错误
* 你的顾虑
## 出问题的的情形:
* 字段添加,导致旧表格字段与新的不匹配引发 ``android.database.sqlite.SQLiteException`` 类异常。
* 服务端数据返回无法与就表格匹配,无法进行插入操作
第一个情况会直接导致 APP 闪退掉,第二种就是数据不匹配。
## 几个事实
* GreenDao 目前的 3.+ 版,自动生成的代码的升级方式都是``先删除原来的表格,再创建新的``
```java
/** 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
### 调用例子
```java
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
```java
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
```java
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 , 且成功运行至今。