Problem description

Some time ago, I had a project in the company that needed to update the version. Among the new functional requirements, we need to add a field to the Entity class in Greendao. In gradle, you need to change the greendao version number and rebuild the project.

@Entity
public class BibleInspirationDB implements Serializable {

    static final long serialVersionUID = -15515456L;
    @Id(autoincrement = true) private Long _id; private String bibleId; // BookId private String bookName; // private String InspirationContent; // Inspiration text private String rollTitle; private String rollIndex; Private String secIndex; Private String partIndex; Private long saveTime; private long saveTime; private String content; private String netId; private StringtestId; Private Boolean isSelected; //----> add column @TRANSIENT; // .Copy the code

Update the Greendao version

Greendao {/ / specify the database schema version number, the migration operations such as use schemaVersion 16 / / amend the last version of the 15 to 16}Copy the code

However, an error message was reported after re-running the project indicating that the new fields could not be found.

The reason:

After several checks, it was found that it was written in the greendao version when configuring

android{ .... Greendao {/ / specify the database schema version number, the migration operations such as use schemaVersion 16 / / amend the last version of the 15 to 16}}Copy the code

After the version number is changed, it cannot be identified

Supplement:

When some abnormal phenomena are found, and the same phenomena cannot be found online, most of the reasons are caused by some basic configuration problems in the development process. When this setting is often configured, problems occur without being aware of the original cause. This point I hope to keep in mind in the future development process.

A few extra things I learned this time

While searching for the problem on the Internet this time, I found that other bloggers mentioned that the old data would be cleared after greendao’s new fields were updated. Their recommended solution is to copy the old table data to the new table when initializing the database, and then drop the old table. Some foreign open source implementation class, pro test available

public class MigrationHelper {

    private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
    private static MigrationHelper instance;

    public static MigrationHelper getInstance() {
        if (instance == null) {
            instance = new MigrationHelper();
        }
        return instance;
    }

    private static List<String> getColumns(Database db, String tableName) {
        List<String> columns = new ArrayList<>();
        Cursor cursor = null;
        try {
            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
            if(cursor ! = null) { columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames())); } } catch (Exception e) { Log.v(tableName, e.getMessage(), e); e.printStackTrace(); } finally {if(cursor ! = null) cursor.close(); }returncolumns; } public void migrate(Database db, Class<? extends AbstractDao<? ,? > >... daoClasses) { generateTempTables(db, daoClasses); DaoMaster.dropAllTables(db,true);
        DaoMaster.createAllTables(db, false); restoreData(db, daoClasses); } private void generateTempTables(Database db, Class<? extends AbstractDao<? ,? > >... daoClasses) {for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);

            String divider = "";
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            ArrayList<String> properties = new ArrayList<>();

            StringBuilder createTableStringBuilder = new StringBuilder();

            createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append("(");

            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;

                if (getColumns(db, tableName).contains(columnName)) {
                    properties.add(columnName);

                    String type = null;

                    try {
                        type = getTypeByClass(daoConfig.properties[j].type);
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    }

                    createTableStringBuilder.append(divider).append(columnName).append("").append(type);

                    if (daoConfig.properties[j].primaryKey) {
                        createTableStringBuilder.append(" PRIMARY KEY");
                    }

                    divider = ",";
                }
            }
            createTableStringBuilder.append(");");
            Log.i("lxq"."Create temporary table SQL statement: + createTableStringBuilder.toString());
            db.execSQL(createTableStringBuilder.toString());

            StringBuilder insertTableStringBuilder = new StringBuilder();

            insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append("(");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(" FROM ").append(tableName).append(";");
            Log.i("lxq".SQL statement to insert data into temporary table+ insertTableStringBuilder.toString()); db.execSQL(insertTableStringBuilder.toString()); } } private 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");
            ArrayList<String> properties = new ArrayList();
            ArrayList<String> propertiesQuery = new ArrayList();
            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;

                if (getColumns(db, tempTableName).contains(columnName)) {
                    properties.add(columnName);
                    propertiesQuery.add(columnName);
                } else {
                    try {
                        if (getTypeByClass(daoConfig.properties[j].type).equals("INTEGER")) {
                            propertiesQuery.add("0 as " + columnName);
                            properties.add(columnName);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            StringBuilder insertTableStringBuilder = new StringBuilder();

            insertTableStringBuilder.append("INSERT INTO ").append(tableName).append("(");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", propertiesQuery));
            insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");

            StringBuilder dropTableStringBuilder = new StringBuilder();

            dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
            Log.i("lxq".SQL statement to insert formal table: + insertTableStringBuilder.toString());
            Log.i("lxq".Alter table temp alter table temp alter table temp+ dropTableStringBuilder.toString()); db.execSQL(insertTableStringBuilder.toString()); db.execSQL(dropTableStringBuilder.toString()); } } private String getTypeByClass(Class<? >type) throws Exception {
        if (type.equals(String.class)) {
            return "TEXT";
        }
        if (type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class) || type.equals(int.class)) {
            return "INTEGER";
        }
        if (type.equals(Boolean.class) || type.equals(boolean.class)) {
            return "BOOLEAN";
        }

        Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString())); exception.printStackTrace(); throw exception; }}Copy the code

Then write a Helper class, rewrite onUpgrade method, inside the realization of database data backup

public class MySqlLiteOpenHelper extends DaoMaster.OpenHelper{ public MySqlLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) { super(context, name); } @Override public void onCreate(Database db) { super.onCreate(db); /** * select * from database (*); /** * from database (*); */} @override public void onUpgrade(Database db, int oldVersion, int newVersion) { super.onUpgrade(db, oldVersion, newVersion); /** * this method will not run */ when the database is created for the first time. This method will not run */ when the database is created for the first timeif(oldVersion < newVersion) { MigrationHelper.getInstance().migrate(db, BibleLineAtionDBDao.class); MigrationHelper.getInstance().migrate(db, BibleInspirationDBDao.class); } } @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { super.onDowngrade(db, oldVersion, newVersion); /** * perform database downgrade * 1, only if the new version is lower than the old version */}}Copy the code

Initialize Greendao

public class DBRepository {
    public static final String DB_NAME = "data-db"; Private static DaoSession mDaoSession; private static MySqlLiteOpenHelper mySqlLiteOpenHelper; /** * Initializes greenDao. This operation is recommended at Application initialization; */ public static void initDatabase(Context context) {if (context == null) {
            throw new IllegalArgumentException("You cannot start a load on a null Context");
        }
//        DatabaseOpenHelper mHelper = new DaoMaster.DevOpenHelper(context.getApplicationContext(), DB_NAME);
//        Database db = mHelper.getWritableDb();
        mySqlLiteOpenHelper = new MySqlLiteOpenHelper(context.getApplicationContext(),DB_NAME,null);
        Database db = mySqlLiteOpenHelper.getWritableDb();
        DaoMaster mDaoMaster = new DaoMaster(db);
        mDaoSession = mDaoMaster.newSession();
    }

    public static DaoSession getDaoSession() {
        returnmDaoSession; }}Copy the code

Implement the initialization in Application

 private void initDataBase() {
        DBRepository.initDatabase(MyApplication.getContext());
    }
Copy the code