Search code examples
androidkotlinandroid-room

Adding second Room database to my app causes exception: " Caused by: java.lang.IllegalStateException: Room cannot verify the data integrity"


I am creating a pixel art editor app and I already have one Room database which stores the users' creations and another Room database which I want to add which will store some custom color palettes the user wants to add to the app.

To do this I added the following database:

@Database(entities = [ColorPalette::class], version = 3)
abstract class ColorPalettesDatabase: RoomDatabase() {
    abstract fun colorPalettesDao(): ColorPalettesDao

    companion object {
        private var instance: ColorPalettesDatabase? = null
        fun getDatabase(context: Context): ColorPalettesDatabase {
            if (instance == null) {
                synchronized(ColorPalettesDatabase::class) {
                    if (instance == null) instance = Room.databaseBuilder(context.applicationContext, ColorPalettesDatabase::class.java, AppData.colorPalettesDBFileName).allowMainThreadQueries().build()
                }
            }
            return instance!!
        }
    }
}

And the DAO:

@Dao
interface ColorPalettesDao {
    @Insert
    suspend fun insertColorPalette(colorPalette: ColorPalette)

    @Query("SELECT * FROM ColorPalette ")
    fun getAllColorPalettes(): LiveData<List<ColorPalette>>

    @Query("DELETE FROM ColorPalette WHERE objId=:colorPaletteId")
    fun deleteColorPalette(colorPaletteId: Int)
}

I added a variable in AppData and initialized it in the MainActivity's onCreate method:

class AppData {
    companion object {
        var pixelArtDBFileName = "pixel_art_db"
        lateinit var pixelArtDB: PixelArtDatabase

        var colorPalettesDBFileName = "color_palettes_db"
        lateinit var colorPalettesDB: ColorPalettesDatabase
    }
}
AppData.colorPalettesDB = ColorPalettesDatabase.getDatabase(this)

And finally, I use get the ColorPalette data from the database in the ColorPalettesFragment.kt file:

class ColorPalettesFragment(private val lifecycleOwner: LifecycleOwner) : Fragment(), ColorPalettesListener {
    private var _binding: FragmentColorPalettesBinding? = null

    private val binding get() = _binding!!

    private lateinit var caller: ColorPalettesFragmentListener

    private fun setUpRecyclerView() {
        binding.apply {
            fragmentColorPalettesRecyclerView.layoutManager = LinearLayoutManager([email protected]).apply {
                orientation = LinearLayoutManager.HORIZONTAL
            }
            AppData.colorPalettesDB.colorPalettesDao().getAllColorPalettes().observe(lifecycleOwner) {
                fragmentColorPalettesRecyclerView.adapter = ColorPalettesAdapter(it, this@ColorPalettesFragment)
            }
        }
    }

    companion object {
        fun newInstance(lifecycleOwner: LifecycleOwner) = ColorPalettesFragment(lifecycleOwner)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is ColorPalettesFragmentListener) caller = context
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        _binding = FragmentColorPalettesBinding.inflate(inflater, container, false)

        setUpRecyclerView()

        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    override fun onColorPaletteTapped(selectedColorPalette: ColorPalette) {
        caller.onColorPaletteTapped(selectedColorPalette)
    }
}

So far I'm testing it when the database is empty, what I expect is whenever the user taps the following button they will see a blank RecyclerView:

enter image description here

Unfortunately, I can't even get to the screen as when I run the app and try to navigate to the Canvas I get the following exception:

2021-12-22 08:55:57.251 24474-24554/com.realtomjoney.pyxlmoose E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_1
    Process: com.realtomjoney.pyxlmoose, PID: 24474
    java.lang.RuntimeException: Exception while computing database live data.
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:920)
     Caused by: java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
        at androidx.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:154)
        at androidx.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:135)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:201)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:427)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:151)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:112)
        at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:706)
        at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:483)
        at androidx.room.RoomDatabase.query(RoomDatabase.java:526)
        at androidx.room.util.DBUtil.query(DBUtil.java:86)
        at com.realtomjoney.pyxlmoose.dao.ColorPalettesDao_Impl$4.call(ColorPalettesDao_Impl.java:108)
        at com.realtomjoney.pyxlmoose.dao.ColorPalettesDao_Impl$4.call(ColorPalettesDao_Impl.java:105)
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:920) 

I've tried to look at other solutions for a fix but nothing seems to help.

This is coming from the ColorPalettesDatabase as when I remove all of its usages in code the exception goes away.

Updating the version unfortunately doesn't help and so doesn't uninstalling the app.


Solution

  • It sounds like you have an existing version of the database and have changed the schema (i.e. changed the ColorPalette class).

    Room has detected this change and is therefore suggesting that you increase the version number. However, you don't appear to have any Migrations therefore changing the version number would then fail.

    IF you have no data (sounds like the case as per So far I'm testing it when the database is empty, what I expect is whenever the user taps the following button they will see a blank RecyclerView:). Coding .fallbackToDestructiveMigration might overcome this issue as in the absence of a Migration it will drop the tables and then create the tables.

    However, the above doesn't answer and so doesn't uninstalling the app.. So at a guess you then have an ongoing issue.

    I would suggest first adding .fallbackToDestructiveMigration and then rerunning, if this fails then edit you question to include the failure.

    Next, assuming a failure, try uninstalling and rerunning and then, assuming it fails, editing your question with the failure under that scenario.


    Demo of problem as per question

    ColorPalette class - see comments regarding runs

    @Entity
    data class ColorPalette(
        /* original V3 code */
        @PrimaryKey
        @ColumnInfo(name = "objId")
        val objId: Long? = null,
        val description: String,
        val color: Long
        /* additional code */
        ,
        val extra: String
    )
    

    ColorPaletteDao (same bar no LiveData)

    @Dao
    interface ColorPalettesDao {
        @Insert
        fun insertColorPalette(colorPalette: ColorPalette)
    
        @Query("SELECT * FROM ColorPalette ")
        fun getAllColorPalettes(): /*LiveData<*/List<ColorPalette>/*>*/ /* for testing without LiveData */
    
        @Query("DELETE FROM ColorPalette WHERE objId=:colorPaletteId")
        fun deleteColorPalette(colorPaletteId: Int)
    }
    

    ColorPalleteDatabase (identical but see later)

    MainActivity to demonstrate

    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            val db = ColorPalettesDatabase.getDatabase(this)
            val dao = db.colorPalettesDao()
            dao.getAllColorPalettes()
        }
    }
    

    Run 1 - only original code for ColorPalette i.e. no extra column.

    • Runs fine database created (as per AppInspection):-

    enter image description here

    Run 2 - introduce the extra column, do nothing else, and run

    • ooops!!!! java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so70444736kotlinroomincreaseversion/a.a.so70444736kotlinroomincreaseversion.MainActivity}: java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

    Run 3 - Increase version number from 3 to 4 and run

    • ooops!!!! java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so70444736kotlinroomincreaseversion/a.a.so70444736kotlinroomincreaseversion.MainActivity}: java.lang.IllegalStateException: A migration from 3 to 4 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)

    Run 4 add .fallbackToDestructiveMigration() to ColorPaletteDatabase and run

    • Runs OK, database :-

    enter image description here

    RESET EVERYTHING BACK to initial state (including uninstalling the App) and run Run 1 and Run 2

    • same issue as per Run 2 as expected.

    Uninstall App but without any changes (so extra column and version 3)

    • runs fine and database has the extra column

    Hence

    Why it is suggested that there are other issues, as both suggested initial fixes :-

    1. increase version along with .fallbackToDestructiveMigration, and
    2. uninstall App and rerun

    both work to correct the reported issue.