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)
my task is to migrate the current architecture of our app (using Cupboard) to Room and I am encountering some issues on the very first piece of work, which is migrating the database objects, written in Java for now (Cupboard only supports Java), to make them work with Room. This is an example:
public class ItemDb {
public Long _id;
public String type;
public String subtype;
public long scheduledTime;
public int iteration;
public String data1;
public String data2;
public String data3;
and this is how it looks like in the database:
What I've done with this entity is creating an #Entity class in Kotlin to be used in Room and it looks like this:
#Entity(tableName = "ItemDb")
data class ItemDb(
#PrimaryKey(autoGenerate = true)
val _id: Long? = 0,
val type: String,
val subtype: String,
val scheduledTime: Long,
val iteration: Int,
val data1: String? = null,
val data2: String? = null,
val data3: String? = null
)
Whenever I try to query the database I get this exception:
Pre-packaged database has an invalid schema: ItemDb(com.example.room.model.items.ItemDb).
Expected:
TableInfo{name='ItemDb', columns={scheduledTime=Column{name='scheduledTime', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, subtype=Column{name='subtype', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, data3=Column{name='data3', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data2=Column{name='data2', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data1=Column{name='data1', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, iteration=Column{name='iteration', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, type=Column{name='type', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='ItemDb', columns={scheduledTime=Column{name='scheduledTime', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, subtype=Column{name='subtype', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data3=Column{name='data3', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data2=Column{name='data2', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data1=Column{name='data1', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, iteration=Column{name='iteration', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='integer', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, type=Column{name='type', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
By comparing the twos I can see that some fields are expected to be marked notNull but they seem to be marked nullable instead even though the Kotlin class is declaring the fields as notNull by avoid using the nullable ?.
What am I doing wrong?
If you need more information regarding the architecture of the Database I can provide them easily. Thanks!
In the error message, the "expected" schema is what Room created from your Kotlin declaration of ItemDb. It correctly shows type, subType, scheduledTime and iteration as non-null columns, with all others nullable.
I've not used Cupboard and am presuming it creates a database from your ItemDb Java declaration. The "found" schema, which reports the schema for the prepackaged Cupboard database, indicates that when Cupboard created the database, all columns were declared nullable (i.e. without NOT NULL).
To use the prepackaged database, you will need to perform a migration step that will create a new table with NOT NULL specified for the applicable fields. You then need to copy all the rows from the ItemDb table in the prepackaged database to the new table, with some table re-names (see example below).
The create-statement for the new table will be something like this:
CREATE TABLE new_ItemDb (_id INTEGER PRIMARY KEY AUTOINCREMENT,
type INTEGER NOT NULL, subType INTEGER NOT NULL, scheduledTime INTEGER NOT NULL,
iteration INTEGER NOT NULL, data1 TEXT, data2 TEXT, data3 TEXT)
The migration operations can be modeled after this example copied from the documentation for migrations:
// Migration from 2 to 3, Room 2.2.0
val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE new_Song (
id INTEGER PRIMARY KEY NOT NULL,
name TEXT,
tag TEXT NOT NULL DEFAULT ''
)
""".trimIndent())
database.execSQL("""
INSERT INTO new_Song (id, name, tag)
SELECT id, name, tag FROM Song
""".trimIndent())
database.execSQL("DROP TABLE Song")
database.execSQL("ALTER TABLE new_Song RENAME TO Song")
}
}
Before investing the time making the code changes described above, you could confirm my analysis by temporarily changing your Kotlin definition of ItemDb to make all fields nullable, then retrying your use of the prepackaged database. I would expect the import to succeed.
I try to pre-populate a database in my Android application, but I get the following error:
java.lang.IllegalStateException: Pre-packaged database has an invalid schema: Animal(com.example.application.db.Animal).
Expected:
TableInfo{name='Animal', columns={gender=Column{name='gender', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, name=Column{name='name', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='Animal', columns={gender=Column{name='gender', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
I really do not understand what's the problem, it seems to be absolute identical.
This is where in the code the error is thrown, when the db schema is compared to the entries it tries to insert:
#Override
protected RoomOpenHelper.ValidationResult onValidateSchema(SupportSQLiteDatabase _db) {
final HashMap<String, TableInfo.Column> _columnsAnimal = new HashMap<String, TableInfo.Column>(2);
_columnsAnimal.put("gender", new TableInfo.Column("gender", "TEXT", false, 0, null, TableInfo.CREATED_FROM_ENTITY));
_columnsAnimal.put("name", new TableInfo.Column("name", "TEXT", true, 1, null, TableInfo.CREATED_FROM_ENTITY));
final HashSet<TableInfo.ForeignKey> _foreignKeysAnimal = new HashSet<TableInfo.ForeignKey>(0);
final HashSet<TableInfo.Index> _indicesAnimal = new HashSet<TableInfo.Index>(0);
final TableInfo _infoAnimal = new TableInfo("Animal", _columnsAnimal, _foreignKeysAnimal, _indicesAnimal);
final TableInfo _existingAnimal = TableInfo.read(_db, "Animal");
if (! _infoAnimal.equals(_existingAnimal)) { <<<<<<<<<this goes wrong, see next picture
return new RoomOpenHelper.ValidationResult(false, "Animal(com.johndoes.animalsounds.db.Animal).\n"
+ " Expected:\n" + _infoAnimal + "\n"
+ " Found:\n" + _existingAnimal);
}
Location where image is thrown
This is my entity class:
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
#Entity(tableName = "Animal")
public class Animal {
#ColumnInfo(name = "gender")
public final String gender;
#PrimaryKey
#NonNull
#ColumnInfo(name = "name")
public final String name;
public Animal(#NonNull String name, String gender) {
this.name = name;
this.gender = gender;
}
}
And this is the SQL that created the sqlite table that I want to preload:
CREATE TABLE "Animal" (
"name" TEXT,
"gender" TEXT,
PRIMARY KEY("name")
);
Any ideas would be highly appreciated, thank you.
as I understood you said your primary key is name but it is absolutely wrong, not even that you defined it as text, the text datatype in database is always nullable you should define a long datatype as your table id and add it as primary key and also remove not null attribute from animal names.
on your sqlite add "name" TEXT NOT NULL
hope this helps
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();
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'