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