What happened if a record doesn't exist with LiveData and Room?

Viewed 901

The Code A is from the project play-billing-samples, you can see here.

I think that "SELECT * FROM gold_status LIMIT 1" maybe return no record, I don't know whether fun getGoldStatus(): LiveData<GoldStatus> is correct statement, I think it should be Code B, right?

Code A

@Dao
interface EntitlementsDao {

    @Query("SELECT * FROM gold_status LIMIT 1")
    fun getGoldStatus(): LiveData<GoldStatus>
    ...
}

Code B

@Dao
interface EntitlementsDao {

    @Query("SELECT * FROM gold_status LIMIT 1")
    fun getGoldStatus(): LiveData<GoldStatus>?
    ...
}
3 Answers

I'm not an expert when it comes to Room and SQLite on android. But I would like to share my own observations and findings with this issue.

@P.Leibner is right that LiveData object itself is returned immediately and it is not null. The content might be null.

If you look at generated code of Dao, you will see it is a Java code and it will return null if there are no records matching your query. Also there are no annotations that would help Kotlin compiler to check for nullability.

Therefore, we don't get compiler error or lint warnings if we return non-null object

// Compiles but we might get NullPointerException at runtime.

fun getGoldStatus(): LiveData<GoldStatus>
// or
fun getGoldStatus(): GoldStatus

But you are going to run into NullPointerExceptions at runtime if you don't have a row in table and start doing something with that object if you mark it as non-null in Kotlin.

Note: this won't happen if you return List<GoldStatus> or LiveData<List<GoldStatus>>. it will just return empty list if there are no rows matching.

I didn't know this myself until I run into these problems. Maybe I missed some explanations when learning about room. We just have to know that room will return object or null if you are querying single row and list or empty list if multiple rows.

I prefer specifying nullability in return type of Dao method. This way I write other code that works with it in null safe way.

I would call this one Code C

// Runtime NullPointerException safe.

fun getGoldStatus(): LiveData<GoldStatus?>
// or
fun getGoldStatus(): GoldStatus?

You can also look at this answer to get more info and see example of generated code by Dao.

Code A is right, since not the data from database is returned but an observable data class holder (LiveData), this will be returned even if there is no record in the table. You can then attach an Observer to this observable, the observer will be triggered once changes to database occur w.r.t. your query (You can find several examples for this in the Android Documentation). The getValue() method of a LiveData object can return null though, if no value has been set yet, but usually you use Observers for changes of the LiveData.

There is not much to tell.

You will receive LiveData(null) - so keep that in mind since there may be a NullPointer. You wont receive further changes of this object, also, since it does not exist.

go with

fun getGoldStatus(): LiveData<GoldStatus?>?

it will be much more safe in any case. (Room with LivaData is still a very buggy combination)

Hope it helps.

Related