Related
I'm doing a migration to create new sql table called: category. I had to do a manual migration. When I run the app, the migration fails because the expected object has indices and the found object doesn't, as below (at the end):
java.lang.IllegalStateException: Migration didn't properly handle: category(data.local.CategoryEntity).
Expected:
TableInfo{name='category', columns={name=Column{name='name', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, color=Column{name='color', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, parentId=Column{name='parentId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[ForeignKey{referenceTable='category', onDelete='CASCADE', onUpdate='CASCADE', columnNames=[parentId], referenceColumnNames=[id]}], indices=[Index{name='index_category_parentId', unique=false, columns=[parentId], orders=[ASC]}]}
Found:
TableInfo{name='category', columns={color=Column{name='color', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, name=Column{name='name', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, parentId=Column{name='parentId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[ForeignKey{referenceTable='category', onDelete='CASCADE', onUpdate='CASCADE', columnNames=[parentId], referenceColumnNames=[id]}], indices=[]}
Here's my CategoryEntity.kt:
#Entity(tableName = "category",
foreignKeys = [ForeignKey(
entity = CategoryEntity::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("parentId"),
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)],
indices=[
Index(value = arrayOf("parentId"))
]
)
data class CategoryEntity(
#PrimaryKey(autoGenerate = true) val id: Int = 0,
#ColumnInfo(name = "name") val name: String,
#ColumnInfo(name = "parentId") val parentId: Int?,
#ColumnInfo(name = "color") val color: String
)
Here's my AppDatabase.kt (I don't know how to make the app connect the foreign key constraint index with the expected result)
#Database(
entities = [
ToDoEntity::class,
CategoryEntity::class
],
version = 2
)
#TypeConverters(Converters::class)
abstract class AppDatabase: RoomDatabase() {
abstract val toDoDao: ToDoDao
abstract val categoryDao: CategoryDao
companion object Migrations {
val M_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"CREATE TABLE IF NOT EXISTS `category` (" +
"`id` INTEGER NOT NULL, " +
"`name` TEXT NOT NULL, " +
"`parentId` INTEGER, " +
"`color` TEXT NOT NULL , " +
"PRIMARY KEY(`id`)," +
"CONSTRAINT `index_category_parentId` FOREIGN KEY(parentId) REFERENCES category(id) ON UPDATE CASCADE ON DELETE CASCADE" +
")"
)
}
}
}
}
How can I define the indices in the manual migration?
Ps: The index cannot be a unique for parentId.
How can I define the indices in the manual migration?
Let Room tell you.
Compile the project (which you have done, but suggested as a step to always ensure that changes made are caught).
In Android View look the java(generated) open it.
Find the sub-directory (open all in turn) that has a file (class) that is the same name as the #Database annotated class but suffixed with _Impl i.e. AppDatabase_Impl
Find the method createAllTables and the SQL for creating all the components, including the indexes ('CREATE INDEX ....'). Copy and paste the SQL and use in the Migration.
This SQL will create the components EXACTLY AS EXPECTED.
Note ignore the room_master_table (create table and insert) these are Room specific and room will create them as required
used to store the hash of the schema which is compared against the compiled hash as part of the processess of detecting schema changes (i.e. changes made to the #Entity annotated classes listed via the entities parameter of the #Database annotation)
I'm trying to do a room migration and add a new table with an index, but it is failing.
Entity:
#Entity(tableName = "ring" ,
foreignKeys = [ForeignKey(entity = BellScheduleRoom::class,
parentColumns = arrayOf("code"),
childColumns = arrayOf("bellScheduleCode"),
onUpdate = ForeignKey.CASCADE,
onDelete = ForeignKey.CASCADE)],
indices = [Index(value = ["bellScheduleCode"])])
data class RingRoom(
#PrimaryKey
#ColumnInfo(name = "id", defaultValue = "0")
var id: Int = 0) {
#ColumnInfo(name = "dayOfWeek", defaultValue = "0")
var dayOfWeek: Int = 0
#ColumnInfo(name = "timeStart", defaultValue = "")
var timeStart: String = ""
#ColumnInfo(name = "durationInSeconds", defaultValue = "0")
var durationInSeconds: Int = 0
#ColumnInfo(name = "repeats", defaultValue = "0")
var repeats: Int = 0
#ColumnInfo(name = "bellScheduleCode", defaultValue = "")
var bellScheduleCode: String = ""
override fun toString(): String {
return "Ring: \n \t id: $id\n \t dayOfWeek: $dayOfWeek\n \t timestart: $timeStart\n \t
durationInSeconds: $durationInSeconds\n \t repeats: $repeats"
}
constructor(id: Int, dayOfWeek: Int, timeStart: String, durationInSeconds: Int, repeats: Int,
bellScheduleCode: String): this(id){
this.dayOfWeek= dayOfWeek
this.timeStart = timeStart
this.durationInSeconds = durationInSeconds
this.repeats = repeats
this.bellScheduleCode = bellScheduleCode
}
}
Migration:
class Migration_1_2 : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE `bell_schedule` (`description` TEXT NOT NULL DEFAULT '', `code` TEXT NOT NULL DEFAULT '' , PRIMARY KEY(`code`))")
database.execSQL("CREATE TABLE `ring` (`dayOfWeek` INTEGER NOT NULL DEFAULT 0, `timeStart` TEXT NOT NULL DEFAULT '', `durationInSeconds` INTEGER NOT NULL DEFAULT 0, `repeats` INTEGER NOT NULL DEFAULT 0, `bellScheduleCode` TEXT NOT NULL DEFAULT '', `id` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`), FOREIGN KEY(`bellScheduleCode`) REFERENCES `bell_Schedule`(`code`) ON UPDATE CASCADE ON DELETE CASCADE )")
database.execSQL("CREATE INDEX `index_ring_bellScheduleCode` ON `ring`(`bellScheduleCode`)")
}
}
When running this migration test I get output that doesn't entirely make sense to me.
class TestMigration1to2 {
private val TEST_DB = "migration-test"
#Rule #JvmField
val helper: MigrationTestHelper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
DatabaseRoom::class.java.canonicalName,
FrameworkSQLiteOpenHelperFactory()
)
#Test
#Throws(IOException::class)
fun migrate1To2() {
var db = helper.createDatabase(TEST_DB, 1).apply {
close()
}
db = helper.runMigrationsAndValidate(TEST_DB, 2, true, Migration_1_2())
}
}
Expected(makes sense):
TableInfo{name='ring', columns={dayOfWeek=Column{name='dayOfWeek', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='0'}, repeats=Column{name='repeats', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='0'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='0'}, durationInSeconds=Column{name='durationInSeconds', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='0'}, timeStart=Column{name='timeStart', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue=''''}, bellScheduleCode=Column{name='bellScheduleCode', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue=''''}}, foreignKeys=[ForeignKey{referenceTable='bell_schedule', onDelete='CASCADE', onUpdate='CASCADE', columnNames=[bellScheduleCode], referenceColumnNames=[code]}], indices=[]}
Found (here's my issue):
found: TableInfo{name='ring', columns={dayOfWeek=Column{name='dayOfWeek', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='0'}, repeats=Column{name='repeats', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='0'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='1'}, durationInSeconds=Column{name='durationInSeconds', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='0'}, timeStart=Column{name='timeStart', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue=''''}, bellScheduleCode=Column{name='bellScheduleCode', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue=''''}}, foreignKeys=[ForeignKey{referenceTable='bell_Schedule', onDelete='CASCADE', onUpdate='CASCADE', columnNames=[bellScheduleCode], referenceColumnNames=[code]}], indices=null}
The part that is confusing me the most is the indices=null part. The found indices are always null no matter what I do. Even if I remove the index entirely the migration still fails because indices are null instead of []. Why is the migration finding null indices?
I know this question is months old, yesterday I've encountered the same problem, and the reason was that I forgot to disable foreign keys when running my migration. I was getting this referenceColumnNames=[null] for a foreign key relation.
Running
PRAGMA foreign_keys=off;
Before migration and
PRAGMA foreign_keys=on;
after it solved the issue. Seems like room was silently failing to assign foreign keys at migration time and then complained about that at runtime.
I am trying to update room database
want to add two columns with an existing database and don't want to lose the data.
My existing table name WordTable
#Entity
data class WordTable(
#PrimaryKey(autoGenerate = true)
var id: Int = 0,
var word: String = "",
var des: String = "",
var bookmark: Boolean = false,
var addByUser: Boolean = false,
var uploaded: Boolean = false)
I want to add this two column, so my code is now
#Entity
data class WordTable(
#PrimaryKey(autoGenerate = true)
var id: Int = 0,
var word: String = "",
var des: String = "",
var ref: String = "Added by user",
var recent: Date = Date(),
var bookmark: Boolean = false,
var addByUser: Boolean = false,
var uploaded: Boolean = false)
Note: I provide date conveter
Create a new table
database.execSQL("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT," +
" word TEXT," +
" des TEXT," +
" ref TEXT," +
" recent INTEGER," +
" bookmark INTEGER," +
" addByUser INTEGER," +
" uploaded INTEGER)")
Copy previous data
database.execSQL("Insert Into USER (id, word, des, bookmark, addByUser, uploaded) Select * From WordTable")
Drop word table
database.execSQL("Drop Table WordTable")
Rename user table to WordTable
database.execSQL("Alter Table USER RENAME TO WordTable")
And I get this error;
Expected:
TableInfo{name='WordTable', columns={addByUser=Column{name='addByUser', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, word=Column{name='word', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0}, bookmark=Column{name='bookmark', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, uploaded=Column{name='uploaded', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1}, ref=Column{name='ref', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0}, des=Column{name='des', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0}, recent=Column{name='recent', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='WordTable', columns={addByUser=Column{name='addByUser', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0}, word=Column{name='word', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, bookmark=Column{name='bookmark', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0}, uploaded=Column{name='uploaded', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1}, ref=Column{name='ref', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, des=Column{name='des', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, recent=Column{name='recent', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
The difference between Expected and Found is
Expected: notNull=true
Found: notNull=false
so I try to modify the create table code
database.execSQL("CREATE TABLE `USER` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `word` TEXT NOT NULL, `des` TEXT NOT NULL, `ref` TEXT NOT NULL, `recent` INTEGER NOT NULL, `bookmark` INTEGER NOT NULL, `addByUser` INTEGER NOT NULL, `uploaded` INTEGER NOT NULL)")
But this time I get this error:
android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: USER.ref (Sqlite code 1299), (OS error - 2:No such file or directory)
at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:742)
I also try alter table and column but get same error.
How can I fix this?
You can copy queries to create tables or modify tables from the generated room database file.
Just double tap shift and look for _Impl.
Your problem is related to NON NULL data types in your tables you need to specify these fields as NON NULL and handle DEFAULT values that need to be assigned during your Room Migration.
You can use the ALTER TABLE Query and simply provide migration
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(#NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `table_name` ADD COLUMN `column_name` {type} DEFAULT {defaultValue} NOT NULL");
}
};
When you try to Alter Table, then you should provide default values to all other records that are already present in the table.
The differences between Expected and Found in your error message are the not null constraints in your table.
If you want the rows to be nullable, define their variables as objects without default values and make a constructor that defines the required rows. Do not instantiate row values at the same time as you instantiate the variables!
If you want the rows to never accept Null, add NOT NULL to the end of the rows in your SQLite query, and you don't have to change your model class.
You dont need crate new table
create an method like:
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(#NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `WordTable` ADD COLUMN ref STRING");
}
};
and call Database create method like:
Room.databaseBuilder(/*parameter want */).addMigrations(MIGRATION_1_2).allowMainThreadQueries().build();
I have written Android Room migrations, but somehow it does not handle it properly.
Here is the error:
Expected:
TableInfo{name='Likes', columns={creatorId=Column{name='creatorId', type='TEXT', notNull=false, primaryKeyPosition=0}, messageId=Column{name='messageId', type='TEXT', notNull=false, primaryKeyPosition=0}, createdAt=Column{name='createdAt', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='TEXT', notNull=false, primaryKeyPosition=0}, dbId=Column{name='dbId', type='INTEGER', notNull=true, primaryKeyPosition=1}, timestamp=Column{name='timestamp', type='TEXT', notNull=false, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='Likes', columns={dbId=Column{name='dbId', type='INTEGER', notNull=true, primaryKeyPosition=1}, creatorId=Column{name='creatorId', type='TEXT', notNull=false, primaryKeyPosition=0}, messageId=Column{name='messageId', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='TEXT', notNull=false, primaryKeyPosition=0}, timestamp=Column{name='timestamp', type='TEXT', notNull=false, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
Here is migration script:
database.execSQL("CREATE TABLE Likes (id TEXT, creatorId TEXT, messageId TEXT,"
+ " timestamp TEXT, dbId INTEGER NOT NULL, PRIMARY KEY(dbId))");
Here is model class:
#Entity(tableName = "Likes")
public class LikeDbModel {
#PrimaryKey private int dbId;
private String id;
private String creatorId;
private String messageId;
private String timestamp;
private String createdAt;
public LikeDbModel() {
}
}
Can anyone help?
It seems you are trying to add the column createdAt.
When you create your database add the line .addMigrations(MIGRATION_1_2) to the creation of the database, which I think you have done.
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
YourDatabase.class, "your_database.db")
.addMigrations(
MIGRATION_1_2
)
.build();
Then implement the migration properly by first dropping the table and then creating it or using an alter table statement.
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase db) {
//Either
db.execSQL("drop table Likes"); //if existing table
db.execSQL("CREATE TABLE Likes (id TEXT, creatorId TEXT, messageId TEXT,"
+ " timestamp TEXT, createdAt TEXT, dbId INTEGER NOT NULL, PRIMARY KEY(dbId))")
//or
db.execSQL("alter table add createdAt TEXT");
}
};
The error you are getting is because room is comparing your entity to the existing table in the database. You could also wrap the migration statements in a try/catch SQLiteException to see what is working.
If you don't want to lose the data in your table, use an alter table instead. If you want to drop/create, the make sure you copy the data over to a new table first with the new structure, copy the data to the new table, drop your original Likes table and rename you new table to Likes. See example here
Room documentation: https://developer.android.com/training/data-storage/room/migrating-db-versions
Its missing the createdAt field in the migration
Java.lang.IllegalStateException
Migration didn't properly handle
user(therealandroid.github.com.roomcore.java.User).
Expected:
TableInfo{name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, age=Column{name='age',
type='INTEGER', notNull=true, primaryKeyPosition=0},
id=Column{name='id', type='INTEGER', notNull=true,
primaryKeyPosition=1}}, foreignKeys=[]} Found:
Found
TableInfo{ name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, id=Column{name='id',
type='INTEGER', notNull=true, primaryKeyPosition=1},
age=Column{name='age', type='INTEGER', notNull=false,
primaryKeyPosition=0}}, foreignKeys=[]}
I'm trying to perform a simple migration, I have a class called Userand it have two columns ID (primary key) and NAME TEXT and then I populate database with two users data, then I add the column AGE in the object User and in the Migration constant I add an alter table to add this new column and lastly I replace version of the database 1 to 2.
Here is the code
User.class
#Entity(tableName = "user")
public class User {
#PrimaryKey
private int id;
#ColumnInfo(name = "name")
private String name;
#ColumnInfo(name = "age")
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Database class
#Database(entities = {User.class}, version = 2)
public abstract class RoomDatabaseImpl extends RoomDatabase {
abstract UserDao userDao();
}
Migration code
public static Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER");
}
};
and it call
Room.databaseBuilder(context, RoomDatabaseImpl.class, "Sample.db")
.addMigrations(MIGRATION_1_2)
.allowMainThreadQueries()
.build();
Before change the object adding AGE and performing the migration I add two register and it works.
After performing the migration, I just tried to add a new User as bellow:
User user = new User();
user.setName("JoooJ");
user.setId(3);
user.setAge(18);
List<User> userList = new ArrayList<>();
userList.add(user);
App.database(this).userDao().insertAll(userList); // The crash happens here
Other informations:
Android Studio 3 and I didn't tested in the actual.
Dependencies:
compile "android.arch.persistence.room:runtime:1.0.0-alpha9-1"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha9-1"
compile "android.arch.persistence.room:rxjava2:1.0.0-alpha9-1"
gradle 2.3.3
Can someone help me please, I realy don't know what im doing wrong or if it is a bug.
The error message is hard to parse, but there's a difference:
TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=[]} Found:
Found
TableInfo{ name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=[]}
Age is nullable but Room expected it to be not null.
Change your migration to:
database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");
Since this exception explanation is VERY difficult to parse, I have created a small script that does the diff for you.
Example:
mig "java.lang.IllegalStateException: Migration failed. expected:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=[]} , found:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=[]}"
Result:
I too wrote a small JS script which you can find https://hrankit.github.io/RoomSQLiteDifferenceFinder/
The process is pretty Simple.
Input the Expected error log in Expected column which is the Left One.
Input the Found error log in Found column which is the Right One.
Press Go. button. The error logs get converted to JSON.
Press Compare button and Voila, you have the difference you need.
This plugin finds out the difference in the two Expected and Found dump from the Android Studio Logcat.
Checkout the image of comparison here
None of the answers are correct in any of the links. After much experiments, found a way for it. The ALTER query needs to be written in the following way to make it work:
database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")
However, the Integer DEFAULT value can be anything.
If you want to add String type column, add in the following manner:
database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")
This works like a charm.
I faced this issue today, I just changed int fields to Integer in Entities. As int cannot be null but Integer objects could be null.
if you want to add Integer type column , add this code
database.execSQL("ALTER TABLE users"
+ " ADD COLUMN year INTEGER NOT NULL DEFAULT 0 ")
If you are getting notNull differences, you can simply mark your class field with #NonNull annotation, or change your sql with ALTER TABLE. But if you are getting column type differences, such as expected: TYPE=TEXT, then found TYPE='' (COLLATE NOCASE), or expected INTEGER, found INT, then the only solution is to drop and recreate your table. Sqlite does not allow changing column types.
Use INTEGER in Sqlite instead of INT and mark your Java entity with #ColumnInfo(collate = NOCASE) (if you use NOCASE in Sqlite).
Take a look at the json file under app\schemas to get the sql for the expected queries.
static final Migration MIGRATION_2_3= new Migration(2, 3) {
#Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("DROP TABLE IF EXISTS table_tmp");
database.execSQL("CREATE TABLE IF NOT EXISTS `table_tmp` ...");
database.execSQL("insert into table_tmp (`id`, `name` , ...");
database.execSQL("DROP INDEX IF EXISTS `index_table_name`");
database.execSQL("CREATE INDEX IF NOT EXISTS `index_table_name` ON `table_tmp` (`name`)");
database.execSQL("DROP TABLE IF EXISTS table");
database.execSQL("alter table table_tmp rename to table");
}
};
I have faced notNull differences in kotlin and found exception like below
Expected:
TableInfo{name='enDic', columns={definition=Column{name='definition', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, favourite=Column{name='favourite', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, word=Column{name='word', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, usage=Column{name='usage', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='enDic', columns={usage=Column{name='usage', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, definition=Column{name='definition', type='text', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, favourite=Column{name='favourite', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, word=Column{name='word', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
Then I used below mentioned code to solve that problem
#Entity(tableName = "enDic")
data class Word(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "_id")
var _id: Int?,
#ColumnInfo(name = "word")
var word: String?,
#ColumnInfo(name = "definition")
var definition: String,
#ColumnInfo(name = "favourite")
var favourite: Int?,
#ColumnInfo(name = "usage")
var usage: Int?
)
Instead of this code
#Entity(tableName = "enDic")
data class Word(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "_id")
var _id: Int,
#ColumnInfo(name = "word")
var word: String,
#ColumnInfo(name = "definition")
var definition: String,
#ColumnInfo(name = "favourite")
var favourite: Int,
#ColumnInfo(name = "usage")
var usage: Int
)
The tool mentioned by #HRankit in his answer: link did not seem to work when I tried it today. If that is the case for you too, please read on:
If anyone is struggling with error message mentioned in the question and have a huge table with lots of columns, you might want to try this online tool to check between expected and found schema.
Also its really important that you decide upon the column names and data types before the <db_version_number>.json (eg: 13.json) file is generated. If the json file was already generated and you made some changes to the Entity class afterwards, you might want to delete the json file and rebuild the project to generate it with correct set of values.
Lastly you should check your sql statements on the migration itself.
I had problems with roomVersion '2.4.0-alpha01', this version not generate Index table for my case
#Entity(
tableName = "SoundRules",
indices = [
Index(value = ["remoteId"], unique = true)
]
)
I resolve problem just updated room version to '2.4.0-alpha03'