很久没写博客了,最近一直很忙,没时间整理,一些内容都保存到草稿箱了,但是比较乱,需要整理后才能发,今天抽时间挑出来一篇,全是源码,描述的内容很少(基本没有,除了代码中的一些注解),相信能用到的朋友一看就能明白的;同时也给自己做个记录,方便以后用到直接拿过去就行了。
这篇的内容主要是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;}@SafeVarargsprivate static boolean generateNewTablesIfNotExists_withNoExchangeData(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {return reflectMethod(db, "createTable", true, daoClasses);}@SafeVarargsprivate 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;}@SafeVarargsprivate static boolean dropAllTables(StandardDatabase db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {return reflectMethod(db, "dropTable", ifExists, daoClasses);}@SafeVarargsprivate 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*/@SafeVarargsprivate 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*/@SafeVarargsprivate 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 listList<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() {@Overridepublic void onFinalSuccess() {LoggUtils.e("MyGreenDaoDbHelper", "进行数据库升级 ===> 成功");}@Overridepublic void onFailedLog(String errorMsg) {LoggUtils.e("MyGreenDaoDbHelper", "升级失败日志 ===> " + errorMsg);}}).compatibleUpdate(db,StudentDao.class, GroupDao.class);LoggUtils.e("MyGreenDaoDbHelper", "进行数据库升级--完成");}}@Overridepublic 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;}@Overridepublic void onCreate() {super.onCreate();context = getApplicationContext();setupDatabase();}/*** 配置数据库*/private void setupDatabase() {//创建数据库shop.dbMyGreenDaoDbHelper 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
}