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
- do not have a 2-3 Migration and/or a 1-3 Migration AND
- 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
- revert to the database that has data
- implement a Migration to cover the Migration from the reverted database version to the new version
- correct the issue below
- rerun
- I would suggest adding some logging (to the migration and to the callbacks
onCreate
and alsoonOpen
andonDestructiveMigration
) 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;
....