Search code examples
androidkotlinmvvmandroid-jetpack-composeandroid-room

Can't add an item to room database


For some reason I can't add an Item to room database , The rest of my Daos are working ok but it seems @Upsert isn't (obviously I'm messing up somewhere). I used the query in "App inspection" to add elements to it and successively viewed what was added making it obvious where the problem was.

The app is a simple app that stores memorised verses. Your help will be highly appreciated.

Daos

interface DaoFunctions {

    @Upsert
    suspend fun addVerse(verse: Verse)


    @Query("SELECT * FROM MyVersesTable ORDER BY bookPosition ASC")
    fun getVerseByBook(): PagingSource<Int,Verse>

    @Query("SELECT * FROM MyVersesTable ORDER BY themeName ASC")
    fun getVersesByTheme(): PagingSource<Int,Verse>

    @Query("SELECT * FROM MyVersesTable ORDER BY date ASC")
    fun getVersesByDate(): PagingSource<Int,Verse>

    @Query("SELECT * FROM MyVersesTable")
    fun getVersesByDateFlow(): List<Verse>

    @Delete
     fun deletVerse(verse: Verse)




} 

Repository Interface

interface DataBaseRepository {


    suspend fun addVerse(verse: Verse)

    fun getVerseByBook(): PagingSource<Int, Verse>

    fun getVersesByTheme(): PagingSource<Int,Verse>

    fun getVersesByDate(): PagingSource<Int,Verse>

    fun deletVerse(verse: Verse)

    fun getVersesByDateFlow(): List<Verse>

}  

Repository Implemented


class DataBaseRepositoryImpl @Inject constructor( val daos: DaoFunctions): DataBaseRepository{

    override suspend fun addVerse(verse: Verse)  = daos.addVerse(verse)

    override fun getVerseByBook() = daos.getVerseByBook()


    override fun getVersesByTheme() = daos.getVersesByTheme()

    override fun getVersesByDate() = daos.getVersesByDate()

    override fun deletVerse(verse: Verse) = daos.deletVerse(verse)

    override fun getVersesByDateFlow() = daos.getVersesByDateFlow()




}

Table


@Entity(tableName = "MyVersesTable")
data class Verse(

    val verse: String,
    val bookName: String,
    val chapterAndVerseNumber: String,

    val bookPosition: Byte,
    val date: Long,


    val themeName: String,

    val photoFilePath: String,




    @PrimaryKey(autoGenerate = true)
    val id: Int = 0
)

Adding Verse View Model


@HiltViewModel
class AddingVerseScreenViewModel @Inject constructor(

    private val daoFunctions: DataBaseRepositoryImpl

): ViewModel() {





    private val _state = MutableStateFlow(AddingVerseScreenStates())
    val state = _state.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), AddingVerseScreenStates())


    private val eventsChannel = Channel<AddingVerseScreenEvents>()
    val eventFlow =  eventsChannel.receiveAsFlow()


    fun triggerSaveVerseEvent(){

        viewModelScope.launch {

            eventsChannel.send(AddingVerseScreenEvents.saveVerse)

        }

    }


    fun triggerShowingPopUpMenuEvent(){

        viewModelScope.launch {

            eventsChannel.send(AddingVerseScreenEvents.showPopUpMenu)

        }

    }





    fun triggerHidingPopUpMenuEvent(){

        viewModelScope.launch {

            eventsChannel.send(AddingVerseScreenEvents.hidePopUpMenu)

        }

    }







    fun saveVerse() {

//        if (_state.value.bookName.isBlank() || _state.value.verse.isBlank() || _state.value.themeName.isBlank() || _state.value.photoFilePath.isBlank() || _state.value.note.isBlank())
//            return


        val verse =  Verse(
            bookName = _state.value.bookName,
            chapterAndVerseNumber = _state.value.chapter + ":" + _state.value.verseNumber,
            verse = _state.value.verse,
            date = System.currentTimeMillis(),
            themeName = _state.value.themeName,
            bookPosition = _state.value.bookPosition,
            photoFilePath = _state.value.photoFilePath,


        ) // VERSE ENDS

        viewModelScope.launch {


            daoFunctions.addVerse(verse)

        } // SCOPE ENDS


//        _state.update {
//
//            it.copy(
//
//                bookName = "",
//                chapterAndVerseNumber = "",
//                verse = "",
//                bookPosition = 0,
//
//                note = "",
//
//                themeName = "",
//                themeColour = "",
//
//                photoFilePath = "",
//
//            ) // COPY ENDS
//
//        } // UPDATE ENDS





    }







    fun showPopUpMenu(){


        _state.update { it.copy(showingPopupMenu = true) }


    }

    fun hidePopUpMenu(){


        _state.update {    it.copy(showingPopupMenu = false)    }


    }



    fun setBookName(book: String){

        _state.update {    it.copy(bookName = book)     }


    }



    fun showBookSelectionDialog(){

        _state.update {    it.copy(isBookSelectionDialogShowing = true)     }
    }





    fun hideBookSelectionDialog(){

        _state.update {    it.copy(isBookSelectionDialogShowing = false)     }
    }







    fun showChapterSelectionDialog(){

        _state.update {    it.copy(isChapterSelectionDialogShowing = true)     }
    }





    fun hideChapterSelectionDialog(){

        _state.update {    it.copy(isChapterSelectionDialogShowing = false)     }
    }




    fun showVerseSelectionDialog(){

        _state.update {    it.copy(isVerseSelectionDialogShowing = true)     }
    }





    fun hideVerseSelectionDialog(){

        _state.update {    it.copy(isVerseSelectionDialogShowing = false)     }
    }






    fun setChapter(chapter: String){

        _state.update {    it.copy(chapter = chapter)     }

    }







    fun setVerseNumber(verseNumber: String){

        _state.update {    it.copy(verseNumber = verseNumber)     }

    }





    fun setVerse(verse: String){

        _state.update {    it.copy(verse = verse)     }

    }


    fun setNote(note: String){

        _state.update {    it.copy(note = note)     }
    }


    fun setThemeName(themeName: String){

        _state.update {    it.copy(themeName = themeName)     }

    }


    fun setThemeColour(colour: String){

        _state.update {    it.copy(themeColour = colour)     }

    }


    fun setPhotoFilePath(path: String){

        _state.update {    it.copy(photoFilePath = path)     }

    }



    fun setConditionForThemeExistence(condition: Boolean){

        _state.update {    it.copy(doesThemeExist = condition)     }
    }


















}

Home Screen View Model

@HiltViewModel
class HomeScreenViewModel @Inject constructor(

    val daoFunctions: DataBaseRepositoryImpl

): ViewModel() {




  private val _state = MutableStateFlow(HomeScreenStates())


  val state = _state.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), HomeScreenStates())


    val allVerses = Pager(

     config = PagingConfig(

         pageSize = 60,
         enablePlaceholders = true,
         maxSize = 200

     ) // PAGING CONFIG ENDS
 ) {

//      when(_state.value.sortType){
//
//          SortType.byBook -> daoFunctions.getVerseByBook()
//          SortType.byDate -> daoFunctions.getVersesByDate()
//          SortType.byTheme -> daoFunctions.getVersesByTheme()
//      }



            daoFunctions.getVersesByDate()



 }// PAGER ENDS


    private val eventsChannel = Channel<HomeScreenEvents>()
    val eventFlow = eventsChannel.receiveAsFlow()


    fun triggerShowingPopUpMenuEvent(){

         viewModelScope.launch {

             eventsChannel.send(HomeScreenEvents.showPopUpMenu)

         }

    }





    fun triggerHidingPopUpMenuEvent(){

        viewModelScope.launch {

            eventsChannel.send(HomeScreenEvents.hidePopUpMenu)

        }

    }






    fun triggerShowingMenuSideBarEvent(){

        viewModelScope.launch {

            eventsChannel.send(HomeScreenEvents.showMenuSideBar)

        }

    }





    fun triggerHidingMenuSideBarEvent(){

        viewModelScope.launch {

            eventsChannel.send(HomeScreenEvents.hideMenuSideBar)

        }

    }






    fun triggerExpandingSearchBarEvent(){

        viewModelScope.launch {

            eventsChannel.send(HomeScreenEvents.expandSearchBar)

        }

    }





    fun triggerCollapsingSearchBarEvent(){

        viewModelScope.launch {

            eventsChannel.send(HomeScreenEvents.collapseSearchBar)

        }

    }







    fun triggerShowingAddVerseFloatingButton(){

        viewModelScope.launch {

            eventsChannel.send(HomeScreenEvents.showAddingVerseFloatingButton)

        }

    }





    fun triggerHidingAddVerseFloatingButton(){

        viewModelScope.launch {

            eventsChannel.send(HomeScreenEvents.showAddingVerseFloatingButton)

        }

    }






    fun triggerChangingSortTypeEvent(sortType: SortType){

        viewModelScope.launch {

            eventsChannel.send(HomeScreenEvents.changeSortTypeOfVersesTo(sortType))

        }


    }






















     fun showPopUpMenu(){


        _state.update { it.copy(showingPopupMenu = true) }


    }

    fun hidePopUpMenu(){


        _state.update {    it.copy(showingPopupMenu = false)    }


    }


    fun showMenuSideBar(){

        _state.update {     it.copy(showingMenuSideBar = false)    }

    }


    fun hideMenuSideBar(){

        _state.update {     it.copy(showingMenuSideBar = false)     }

    }


    fun expandSearchBar(){

        _state.update {      it.copy(expandedSearchBar = true)     }

    }


    fun collapseSearchBar(){

        _state.update {      it.copy(expandedSearchBar = false)     }

    }



    fun showAddingVerseFloatingButton(){

        _state.update {      it.copy(showingAddingVerseFloatingButton = true)     }

    }

    fun hideAddingVerseFloatingButton(){

        _state.update {      it.copy(showingAddingVerseFloatingButton = false)     }

    }



    fun changeSortTypeOfVersesTo(sortType: SortType){


        _state.update {      it.copy(sortType =  sortType)     }

    }


    fun updateUiThemeTo(theme: String){


        _state.update {      it.copy(lastOpenedTheme = theme)     }

    }



}

Gradle Module code

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-kapt'
    id 'com.google.dagger.hilt.android'
}

apply plugin: 'com.android.application'
apply plugin: 'com.google.dagger.hilt.android'

android {
    namespace 'com.example.Sword'
    compileSdk 34

    defaultConfig {
        applicationId "com.example.Sword"
        minSdk 21
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary true
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'


    }
    buildFeatures {
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion '1.5.4'
    }
    packagingOptions {
        resources {
            excludes += '/META-INF/{AL2.0,LGPL2.1}'
        }
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.8.0'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
    implementation 'androidx.activity:activity-compose:1.5.1'
    implementation platform('androidx.compose:compose-bom:2022.10.00')
    implementation 'androidx.compose.ui:ui'
    implementation 'androidx.compose.ui:ui-graphics'
    implementation 'androidx.compose.ui:ui-tooling-preview'
    implementation 'androidx.compose.material3:material3'
    implementation platform('androidx.compose:compose-bom:2023.03.00')
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00')
    androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
    androidTestImplementation platform('androidx.compose:compose-bom:2023.03.00')
    debugImplementation 'androidx.compose.ui:ui-tooling'
    debugImplementation 'androidx.compose.ui:ui-test-manifest'

    // ViewModel Compose
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1"

     //FOR ROOM
    implementation "androidx.room:room-ktx:2.5.2"
    implementation("androidx.room:room-paging:2.5.2")
    kapt "androidx.room:room-compiler:2.5.2"


//    //Dagger - Hilt
//    implementation "com.google.dagger:daggerHilt-android:2.40.5"
//    kapt "com.google.dagger:daggerHilt-android-compiler:2.40.5"
//    implementation "androidx.daggerHilt:daggerHilt-lifecycle-viewmodel:1.0.0-alpha03"
//    kapt "androidx.daggerHilt:daggerHilt-compiler:1.0.0"
//    implementation 'androidx.daggerHilt:daggerHilt-navigation-compose:1.0.0'
//    //


    //HILT
    implementation 'com.google.dagger:hilt-android:2.49'
    kapt 'com.google.dagger:hilt-compiler:2.49'
    implementation 'androidx.fragment:fragment-ktx:1.6.2'


    // For instrumentation tests
    androidTestImplementation  'com.google.dagger:hilt-android-testing:2.49'
    kaptAndroidTest 'com.google.dagger:hilt-compiler:2.49'

    // For local unit tests
    testImplementation 'com.google.dagger:hilt-android-testing:2.49'
    kaptTest 'com.google.dagger:hilt-compiler:2.49'



    //PAGER
    def paging_version = "3.2.1"

    implementation "androidx.paging:paging-runtime:$paging_version"

    // alternatively - without Android dependencies for tests
    testImplementation "androidx.paging:paging-common:$paging_version"
    
    // optional - Jetpack Compose integration
    implementation "androidx.paging:paging-compose:3.3.0-alpha02"

    // collect as state with lifecycle
    implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")



}

hilt {
    enableAggregatingTask = false
}

kapt {
    correctErrorTypes true
}

Gradle Project code

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        // other repositories...
        mavenCentral()
    }
    dependencies {
        // other plugins...
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.49'
    }
}



plugins {

    id 'com.google.dagger.hilt.android' version '2.49' apply false
    id 'com.android.application' version '8.1.2' apply false
    id 'com.android.library' version '8.1.2' apply false
    id 'org.jetbrains.kotlin.android' version '1.9.20' apply false

}

Tried everything I can think of, Rewriting everything, Double checking , EVEVERYTHING.

I'm using daggerhilt so my suspicion is that somehow the dao object is being shared to my screen where I view the items and not to the screen where I add the item


Solution

  • I tried out your code. The best and fast possible solution for this is.

    1. Updated Room version. Diff screenshot

    2. Removed return type Unit assignment for Dao method addVerse enter image description here

    Result enter image description here

    Reason: I read some posts on stackoverflow and reddit about it. When I was trying to remove return type Unit assignment for Dao method addVerse without updating the room library then I was getting this error. Type of the parameter must be a class annotated with @Entity or a collection/array of it

    I found out that there was an open issue for this : https://stackoverflow.com/a/73304737/7613626