Home > OS >  While adding a new column in database the old data gets lost. How to keep old data even after databa
While adding a new column in database the old data gets lost. How to keep old data even after databa

Time:11-11

I have migrated room database from version 1 to 2 but I was facing a problem. I solved it after seeing this post : java.lang.IllegalStateException: Migration didn't properly handle:. The app was opening but old data is getting lost. Please suggest me a way to keep old data even after migration. I am posting my migraton code and model below for reference.

CODE

CourseModel.java

package com.gtappdevelopers.gfgroomdatabase;

import android.graphics.Bitmap;
import android.icu.text.UFormat;

import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

import java.text.SimpleDateFormat;
import java.util.Date;

//below line is for setting table name.
@Entity(tableName = "course_table")
public class CourseModal {

    //below line is to auto increment id for each course.
    @PrimaryKey(autoGenerate = true)

    private int EnrollmentStatus;

    //variable for our id.
    private int id;
    private String firstName;
    private String middleName;
    private String lastName;
    private String DOB;
    private String gender;
    private String address;
    private String designation;
    private String email;
    private String phoneNumber;

    @ColumnInfo(typeAffinity = ColumnInfo.BLOB)
    byte[] image;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getMiddleName() {
        return middleName;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getDOB() {
        return DOB;
    }

    public void setDOB(String DOB) {
        this.DOB = DOB;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getDesignation() {
        return designation;
    }

    public void setDesignation(String designation) {
        this.designation = designation;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public byte[] getImage() {
        return image;
    }

    public void setImage(byte[] image) {
        this.image = image;
    }

    public int getEnrollmentStatus() {
        return EnrollmentStatus;
    }

    public void setEnrollmentStatus(int enrollmentStatus) {
        EnrollmentStatus = enrollmentStatus;
    }


    //below line we are creating constructor class.
    //inside constructor class we are not passing our id because it is incrementing automatically
    public CourseModal(String firstName, String middleName, String lastName,String DOB,String gender,String address,String designation,String email,String phoneNumber,Bitmap image) { //,Bitmap image

        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
        this.DOB = DOB;
        this.gender = gender;
        this.address = address;
        this.designation = designation;
        this.email = email;
        this.phoneNumber = phoneNumber;
        this.image = DataConverter.convertImageToByteArray(image);
    }
    public CourseModal(){}
}

CourseDatabase.java

package com.gtappdevelopers.gfgroomdatabase;

import android.content.Context;
import android.os.AsyncTask;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;

//adding annotation for our database entities and db version.
@Database(entities = {CourseModal.class}, version = 3)
//@TypeConverters((ImageBitmapString.class))
public abstract class CourseDatabase extends RoomDatabase {
    //below line is to create instance for our database class.
    private static CourseDatabase instance;

    //below line is to create abstract variable for dao.
    public abstract Dao Dao();

    //upgrade database (add new changes in db via update)
    static Migration MIGRATION_1_2 = new Migration(1,2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {

            database.execSQL("CREATE TABLE course_table_new1("  
                    "   id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"  
                    "   firstName TEXT,"  
                    "   middleName TEXT,"  
                    "   lastName TEXT,"  
                    "   DOB TEXT,"  
                    "   gender TEXT,"  
                    "   address TEXT,"  
                    "   designation TEXT,"  
                    "   email TEXT,"  
                    "   phoneNumber TEXT,"  
                    "   EnrollmentStatus INTEGER NOT NULL DEFAULT 0,"  
//                    "   dateInserted TEXT,"  
                    "   image BLOB);");

            //copy old data into new table
            database.execSQL("INSERT INTO course_table_new1(id,firstName,middlename,lastname,DOB,gender,address,designation,email,phoneNumber,image) SELECT id,firstName,middlename,lastname,DOB,gender,address,designation,email,phoneNumber,image FROM course_table");

            //remove old table
            database.execSQL("DROP TABLE course_table");

            //rename the table
            database.execSQL("ALTER TABLE course_table_new1 RENAME TO course_table");
        } 
    };

    //on below line we are getting instance for our database.
    public static synchronized CourseDatabase getInstance(Context context) {
        //below line is to check if the instance is null or not.
        if (instance == null) {
            //if the instance is null we are creating a new instance
            instance =
                    //for creating a instance for our database we are creating a database builder and passing our database class with our database name.
                    Room.databaseBuilder(context.getApplicationContext(),
                            CourseDatabase.class, "course_database")
                            //below line is use to add fall back to destructive migration to our database.
                            .fallbackToDestructiveMigration()
                            //below line is to add callback to our database.
                            .addCallback(roomCallback)
                            //below line is to build our database.
                            .addMigrations(MIGRATION_1_2)
                            //below line is to upgrade our database.
                            .build();
        }
        //after creating an instance we are returning our instance
        return instance;
    }

    //below line is to create a callback for our room database.
    private static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {
        @Override
        public void onCreate(@NonNull SupportSQLiteDatabase db) {
            super.onCreate(db);
            //this method is called when database is created and below line is to populate our data.
            new PopulateDbAsyncTask(instance).execute();
        }
    };

    //we are creating an async task class to perform task in background.
    private static class PopulateDbAsyncTask extends AsyncTask<Void, Void, Void> {

        PopulateDbAsyncTask(CourseDatabase instance) {
            Dao dao = instance.Dao();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            return null;
        }
    }
}

I was expecting a working app with old data present in the database and new column added in the database.

CodePudding user response:

It looks as though your issue is that you

  1. do not have a 2-3 Migration and/or a 1-3 Migration AND
  2. that the version number has been increased to 3

thus that the fallbackToDestructiveMigration has been actioned.

i.e. you have

  • @Database(entities = {CourseModal.class}, version = 3), and
  • .fallbackToDestructiveMigration(), and ONLY
  • .addMigrations(MIGRATION_1_2)

The fallbackToDestructiveMigration will then drop the course_table and then create it and it will be empty.

You need to

  1. revert to the database that has data
  2. implement a Migration to cover the Migration from the reverted database version to the new version
  3. correct the issue below
  4. rerun
  • I would suggest adding some logging (to the migration and to the callbacks onCreate and also onOpen and onDestructiveMigration) so that you can see what is/isn't running. That would allow you to then determine what has or hasn't been done/run.

Furthermore, if you fix the above then you have issues with the CREATE TABLE course_table_new1( .... as that does not correspond to the @Entity

That is that:-

@Entity(tableName = "course_table")
public class CourseModal {

    //below line is to auto increment id for each course.
    @PrimaryKey(autoGenerate = true)

    private int EnrollmentStatus;

    //variable for our id.
    private int id;
    ....

Defines the table with the EnrollmentStatus column as the Primary Key, whilst:-

id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,

defines the id column as the Primary Key.

If the Migration were to run then it would result in a Room unable to handle Migration exception as the actual table found would not be the expected schema as per the @Entity annotated definition of the table.

I believe that you need to use:-

@Entity(tableName = "course_table")
public class CourseModal {

    //below line is to auto increment id for each course.
    @PrimaryKey(autoGenerate = true)
    //variable for our id.
    private int id;

    private int EnrollmentStatus;
    ....

    
  • Related