Get item by id in Room

25,236

Check again your DAO implementation, the argument must be the same in both, the function parameter and the annotation arg.

Change this:

@Query("SELECT * FROM events WHERE id=:arg0")
fun loadSingle(id: String): LiveData<Event>

To:

@Query("SELECT * FROM events WHERE id=:id ")
fun loadSingle(id: String): LiveData<Event>
Share:
25,236
P. Savrov
Author by

P. Savrov

Updated on October 06, 2020

Comments

  • P. Savrov
    P. Savrov over 3 years

    I'm using Room + LiveData in my Android project. Following to Google Blueprints, I've implemented data layer of my application.

    This is how my Dao looks like:

    @Query("SELECT * FROM events WHERE id=:arg0")
        fun loadSingle(id: String): LiveData<Event>
    

    I'm calling it from my EventRepository:

    fun loadSingle(eventId: String): LiveData<RequestReader<Event>> {
            return object: NetworkManager<Event, Event>(appExecutors!!) {
    
                override fun loadLocal(): LiveData<Event> {
                    val item = eventLocal!!.loadSingle("Title 1")
                    Crashlytics.log(Log.VERBOSE, TAG, "loadFromServer::loadLocal=$item")
                    return item
                }
    
                override fun isUpdateForced(data: Event?): Boolean {
                    Crashlytics.log(Log.VERBOSE, TAG, "loadFromServer::isUpdateForced")
                    return data == null || requestTimeout.isAllowed(UNDEFINED_KEY.toString())
                }
    
                override fun makeRequest(): LiveData<ApiResponse<Event>> {
                    Crashlytics.log(Log.VERBOSE, TAG, "loadFromServer::makeRequest")
                    return Database.createService(EventRemote::class.java).load(eventId)
                }
    
                override fun onSuccess(item: Event) {
                    eventLocal?.save(item)
                }
    
                override fun onFail() {
                    Crashlytics.log(Log.VERBOSE, TAG, "loadFromServer::onFail")
                    requestTimeout.reset(UNDEFINED_KEY.toString())
                }
    
            }.liveData
        }
    

    Where NetworkManager class is (has been "taken" from here):

        abstract class NetworkManager<ResultType, RequestType> @MainThread constructor(val appExecutors: AppExecutors) {
    
            companion object {
                private val TAG = "TAG_NETWORK_MANAGER"
            }
    
            val liveData: MediatorLiveData<RequestReader<ResultType>> = MediatorLiveData()
    
            init {
                liveData.value = RequestReader.loading(null)
                val localSource: LiveData<ResultType> = loadLocal()
                Log.d(TAG, "before add::localSource=${localSource.value}")
                liveData.addSource(localSource, { data ->
                    Log.d(TAG, "data=$data")
                    liveData.removeSource(localSource)
                    if (isUpdateForced(data)) {
                        loadRemote(localSource)
                    } else {
                        liveData.addSource(localSource, { reusedData -> liveData.value = RequestReader.success(reusedData)})
                    }
                })
            }
    
            private fun loadRemote(localSource: LiveData<ResultType>) {
                val remoteSource = makeRequest()
                liveData.addSource(localSource, {
                    liveData.value = RequestReader.success(it)
                })
                liveData.addSource(remoteSource) { response ->
                    liveData.removeSource(localSource)
                    liveData.removeSource(remoteSource)
                    if (response!!.isSuccessful) {
                        appExecutors.diskIO.execute {
                            onSuccess(processResponse(response))
                            appExecutors.mainThread.execute {
                                liveData.addSource(localSource, {
                                    liveData.value = RequestReader.success(it)
                                })
                            }
                        }
                    } else {
                        onFail()
                        liveData.addSource(localSource, {
                            liveData.value = RequestReader.error("Error: ${response.errorMessage}", it)
                        })
                    }
                }
    
            }
    
            @MainThread
            protected abstract fun loadLocal(): LiveData<ResultType>
    
            @MainThread
            protected abstract fun isUpdateForced(data: ResultType?): Boolean
    
            @MainThread
            protected abstract fun makeRequest(): LiveData<ApiResponse<RequestType>>
    
            @WorkerThread
            protected abstract fun onSuccess(item: RequestType)
    
            @MainThread
            protected abstract fun onFail()
    
            @WorkerThread
            protected fun processResponse(response: ApiResponse<RequestType>): RequestType {
            return response.body!!
        }
    }
    

    And after i expect to get my LiveData in ViewModel:

    open class EventSingleViewModel: ViewModel(), RepositoryComponent.Injectable {
    
        companion object {
            private val TAG = "TAG_EVENT_SINGLE_VIEW_MODEL"
        }
    
        @Inject lateinit var eventRepository: EventRepository
    
        var eventSingle: LiveData<RequestReader<Event>>? = null
    
        override fun inject(repositoryComponent: RepositoryComponent) {
            repositoryComponent.inject(this)
            eventSingle = MutableLiveData<RequestReader<Event>>()
        }
    
        fun load(eventId: String) {
            Crashlytics.log(Log.VERBOSE, TAG, "starts to loadList::eventId=$eventId")
            eventSingle = eventRepository.loadSingle(eventId)
        }
    
    }
    

    The problem. I'm getting a list of events the same way (it works!) I've described above, but with a single event (this event is already in database) it doesn't work. I've found out that localSource.value is null (in NetworkManager). Maybe my query is bad or.. something else.