1. 程式人生 > >【轉載】Android Greendao 資料庫升級,新增欄位,資料丟失

【轉載】Android Greendao 資料庫升級,新增欄位,資料丟失


專案使用到Greendao當做資料庫,升級的時候遇到個問題,但新新增int型別欄位的時候,資料遷移報錯了,檢視到是因為新增int、long型別欄位的時候,給的是NOT NULL,就是說不能為空。

   資料庫升級方案使用的是外國某個大神寫的程式碼,大概思路是:

1、新建一張跟原先表一樣的臨時表,同時把資料也複製進去。

2、刪除原先表

3、新建最新的表,將臨時表資料複製進去

4、刪除臨時表

問題就出在第三不,如果臨時表有1個欄位。新表有2個欄位,而且新多的欄位為int型別,那麼複製資料的時候,欄位二(新增)是存NULL,但是其屬性是不能為NULL,這就異常了,這裡有個解決思路:在臨時表上也給多加一個一樣的欄位,然後給預設值,因為有了預設值,所有當複製資料的時候就不會是NULL,親測,可行。

程式碼:

    openHelper:

public class ReleaseOpenHelper extends DaoMaster.OpenHelper {
    Class<? extends AbstractDao<?,?>>[] daoClasses;
    public ReleaseOpenHelper(Context context, String name,Class<? extends AbstractDao<?, ?>>... daoClasses) {
        super(context, name);
        this
.daoClasses = daoClasses; } public ReleaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,Class<? extends AbstractDao<?, ?>>... daoClasses) { super(context, name, factory); this.daoClasses = daoClasses; } @Override public void onCreate
(Database db) { super.onCreate(db); } @Override public void onUpgrade(Database db, int oldVersion, int newVersion) { // super.onUpgrade(db, oldVersion, newVersion); if(oldVersion < newVersion){ MigrationHelper.migrate(db,daoClasses); } } }

   Manager:

public class DatabaseManager {
    public static final String mDBName = "xxx.db";
    private DaoSession mDaoSession = null;
    private NavModelDao favModelDao = null;
    private VideoModelDao videoModelDao = null;
    private VideoStatusModelDao videoStatusModelDao = null;
    private PlayListDao playListDao = null;
    private PlaylistDetailListDao playlistDetailListDao;
    private DatabaseManager() {
    }

    public DatabaseManager init(Context context,Class<? extends AbstractDao<?, ?>>... daoClasses) {
        initDao(context,daoClasses);
        return this;
}

    private static final class Holder {
        private static final DatabaseManager INSTANCE = new DatabaseManager();
}

    public static DatabaseManager getInstance() {
        return Holder.INSTANCE;
}

    private void initDao(Context context,Class<? extends AbstractDao<?, ?>>... daoClasses) {
        final ReleaseOpenHelper helper = new ReleaseOpenHelper(context, mDBName,daoClasses);
        final Database db = helper.getWritableDb();
mDaoSession = new DaoMaster(db).newSession();
favModelDao = mDaoSession.getNavModelDao();
videoModelDao = mDaoSession.getVideoModelDao();
videoStatusModelDao = mDaoSession.getVideoStatusModelDao();
playListDao = mDaoSession.getPlayListDao();
playlistDetailListDao = mDaoSession.getPlaylistDetailListDao();
}

    public final NavModelDao getNavDao() {
        return favModelDao;
}
    public final VideoModelDao getVideoModelDao() {
        return videoModelDao;
}
    public final VideoStatusModelDao getVideoStatusModelDao() {
        return videoStatusModelDao;
}
    public final PlayListDao getVideoPlayListDao() {
        return playListDao;
}
    public final PlaylistDetailListDao getVideoPlayListDetailDao() {
        return playlistDetailListDao;
}
}
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.NonNull;
import android.text.TextUtils;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MigrationHelper {
    public static boolean DEBUG = true;
    private static final String SQLITE_MASTER = "sqlite_master";
    private static final String SQLITE_TEMP_MASTER = "sqlite_temp_master";
    public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        Database database = new StandardDatabase(db);
migrate(database, daoClasses);
}

    public static void migrate(Database database, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        generateTempTables(database, daoClasses);
dropAllTables(database, true, daoClasses);
createAllTables(database, false, daoClasses);
restoreData(database, daoClasses);
}

    private static void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            String tempTableName = null;
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String tableName = daoConfig.tablename;
            if (!isTableExists(db, false, tableName)) {
                continue;
}
            try {
                tempTableName = daoConfig.tablename.concat("_TEMP");
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";");
db.execSQL(dropTableStringBuilder.toString());
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName);
insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");
db.execSQL(insertTableStringBuilder.toString());
} catch (SQLException e) {

            }
        }
    }

    private static boolean isTableExists(Database db, boolean isTemp, String tableName) {
        if (db == null || TextUtils.isEmpty(tableName)) {
            return false;
}
        String dbName = isTemp ? SQLITE_TEMP_MASTER : SQLITE_MASTER;
String sql = "SELECT COUNT(*) FROM " + dbName + " WHERE type = ? AND name = ?";
Cursor cursor=null;
        int count = 0;
        try {
            cursor = db.rawQuery(sql, new String[]{"table", tableName});
            if (cursor == null || !cursor.moveToFirst()) {
                return false;
}
            count = cursor.getInt(0);
} catch (Exception e) {
            e.printStackTrace();
} finally {
            if (cursor != null)
                cursor.close();
}
        return count > 0;
}


    private static String getColumnsStr(DaoConfig daoConfig) {
        if (daoConfig == null) {
            return "no columns";
}
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < daoConfig.allColumns.length; i++) {
            builder.append(daoConfig.allColumns[i]);
builder.append(",");
}
        if (builder.length() > 0) {
            builder.deleteCharAt(builder.length() - 1);
}
        return builder.toString();
}


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

    private static void createAllTables(Database db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        reflectMethod(db, "createTable", ifNotExists, daoClasses);
}

    /**
     * dao class already define the sql exec method, so just invoke it
     */
private static void reflectMethod(Database db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        if (daoClasses.length < 1) {
            return;
}
        try {
            for (Class cls : daoClasses) {
                Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class);
method.invoke(null, db, isExists);
}
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
} catch (InvocationTargetException e) {
            e.printStackTrace();
} catch (IllegalAccessException e) {
            e.printStackTrace();
}
    }

    private static void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
            if (!isTableExists(db, true, tempTableName)) {
                continue;
}


            try {
                // 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)) {
                        StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("ALTER TABLE "+tempTableName +" ADD COLUMN "+columnName+
                                getTableType(daoConfig.properties[j].type));
db.execSQL(insertTableStringBuilder.toString());
}
                    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);
insertTableStringBuilder.append(";");
db.execSQL(insertTableStringBuilder.toString());
}
                StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(dropTableStringBuilder.toString());
} catch (SQLException e) {
            }
        }
    }

    private static List<String> getColumns(Database 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) {
            e.printStackTrace();
} finally {
            if (cursor != null)
                cursor.close();
            if (null == columns)
                columns = new ArrayList<>();
}
        return columns;
}



    private static Object getTableType(Class<?> type){
        if(type.equals(int.class)){
            return " INTEGER DEFAULT 0";
}
        if(type.equals(long.class)){
            return " Long DEFAULT 0";
}
        if(type.equals(String.class)){
            return " TEXT ";
}
        if(type.equals(boolean.class)){
            return " NUMERIC DEFAULT 0";
}
        return " TEXT";
}
}

    Application :

public class MyApplication extends Application {
    @Override
public void onCreate() {
        super.onCreate();
DatabaseManager.getInstance().init(
                this,
NavModelDao.class,
PlayListDao.class,
PlaylistDetailListDao.class,
VideoModelDao.class,
VideoStatusModelDao.class);
}