Vinay Sajip
04/01/2025, 5:39 PMChrimaeon
04/01/2025, 6:07 PMVinay Sajip
04/01/2025, 7:16 PMSQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .schema product
CREATE TABLE IF NOT EXISTS "product" (
"id" INTEGER NOT NULL,
"slug" TEXT NOT NULL UNIQUE,
"label" TEXT NOT NULL UNIQUE,
"price" NUMERIC NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
Here are my entity definition corresponding to this table, the DAO to access it, the ViewModel
and the database setup:
@Entity(tableName = "product")
data class Product(
@PrimaryKey(autoGenerate = true)
val id: Int,
val slug: String,
val label: String,
val price: Double,
@Ignore val quantity: Int,
) {
constructor(id: Int, slug: String, label: String, price: Double) : this(id, slug, label, price, 0)
}
@Dao
interface ProductData {
@Query("SELECT * FROM product")
fun getAllProducts(): List<Product>
}
class ProductViewModel(private val data: ProductData) : ViewModel() {
val products = data.getAllProducts()
}
@Database(
version = 1,
entities = [Product::class],
exportSchema = false,
)
abstract class AppDatabase : RoomDatabase() {
abstract val product: ProductData
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getInstance(context: Context): AppDatabase {
synchronized(this) {
return INSTANCE ?: Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"viscount.db"
)
.createFromAsset("data/viscount.db")
.build().also { INSTANCE = it }
}
}
}
}
here is how I set it up in my main activity:
class MainActivity : ComponentActivity() {
private val db by lazy { AppDatabase.getInstance(this) }
private val viewModel by viewModels<ProductViewModel>(
factoryProducer = {
object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ProductViewModel(db.product) as T
}
}
}
)
// other code omitted for brevity
}
The error occurs on the line return ProductViewModel(db.product) as T
and here is a partial log from LogCat:
java.lang.IllegalStateException: Pre-packaged database has an invalid schema: product(com.red_dove.viscount.Product).
Expected:
TableInfo{name='product', columns={id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}, label=Column{name='label', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, slug=Column{name='slug', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, price=Column{name='price', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='product', columns={id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='undefined'}, slug=Column{name='slug', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, label=Column{name='label', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, price=Column{name='price', type='NUMERIC', affinity='1', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}}, foreignKeys=[], indices=[]}
at androidx.room.RoomOpenHelper.onCreate(RoomOpenHelper.kt:73)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onCreate(FrameworkSQLiteOpenHelper.kt:244)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:432)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:336)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableOrReadableDatabase(FrameworkSQLiteOpenHelper.kt:232)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.innerGetDatabase(FrameworkSQLiteOpenHelper.kt:190)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getSupportDatabase(FrameworkSQLiteOpenHelper.kt:151)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.kt:104)
at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.kt:71)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.kt:632)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.kt:451)
at com.red_dove.viscount.ProductData_Impl.getAllProducts(ProductData_Impl.java:31)
at com.red_dove.viscount.ProductViewModel.<init>(Data.kt:33)
at com.red_dove.viscount.MainActivity$viewModel$2$1.create(MainActivity.kt:27)
The only relevant difference I can see in the "expected" vs "found" lines is that we're expecting
id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}
but finding
id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='undefined'}
Can anyone explain why it finds notNull=false
when by the table definiion clearly it should be marked as notNull=true
, the expected value?darkmoon_uk
04/03/2025, 9:38 PM