Partial Entities

Prior to the introduction of support for SQL default values, we always had to provide values for all columns in our INSERT statements. If you inserted solely via @Insert, you might not notice this limitation. The fact that we now have SQL default value support means that the “insert-by-@Query” option has greater flexibility: we do not have to provide all values for all columns.

Related to that is partial entity support, where we can have ordinary @Insert DAO functions also only supply a subset of properties to go into the to-be-inserted row.

We covered earlier in the book how we can use arbitrary POJOs or Kotlin data classes for the output of @Query functions:

package com.commonsware.room.misc

import androidx.room.*
import kotlin.random.Random

data class CountAndSumResult(val count: Int, val sum: Long)

@Entity(tableName = "aggregate")
class AggregateEntity(
  @PrimaryKey(autoGenerate = true)
  val id: Long = 0,
  val value: Long = Random.nextLong(1000000)
) {
  @Dao
  interface Store {
    @Query("SELECT * FROM aggregate")
    fun loadAll(): List<AggregateEntity>

    @Query("SELECT COUNT(*) FROM aggregate")
    fun count(): Int

    @Query("SELECT COUNT(*) as count, SUM(value) as sum FROM aggregate")
    fun countAndSum(): CountAndSumResult

    @Insert
    fun insert(entities: List<AggregateEntity>)
  }
}

Here, we use CountAndSumResult to model the response from:

SELECT COUNT(*) as count, SUM(value) as sum FROM aggregate

Similarly, we can use a POJO or Kotlin data class to model partial input to an @Insert, @Update, or @Delete function:

package com.commonsware.room.misc

import androidx.room.*

data class PartialDefaultValue(
  val id: String,
  val title: String
)

@Entity(tableName = "defaultValue")
class DefaultValueEntity(
  @PrimaryKey
  val id: String,
  val title: String,
  @ColumnInfo(defaultValue = "something")
  val text: String? = null,
  @ColumnInfo(defaultValue = "123")
  val version: Int = 1
) {
  @Dao
  interface Store {
    @Query("SELECT * FROM defaultValue")
    fun loadAll(): List<DefaultValueEntity>

    @Query("SELECT * FROM defaultValue where id = :id")
    fun findByPrimaryKey(id: String): DefaultValueEntity

    @Insert
    fun insert(entity: DefaultValueEntity)

    @Query("INSERT INTO defaultValue (id, title) VALUES (:id, :title)")
    fun insertByQuery(id: String, title: String)

    @Insert(entity = DefaultValueEntity::class)
    fun insertPartial(partial: PartialDefaultValue)
  }
}

In addition to the insert() and insertByQuery() DAO functions, we have insertPartial(). This accepts a PartialDefaultValue object and uses that for an INSERT into our table.

By default, this would fail at compile time, with the Room annotation processor complaining that PartialDefaultValue is not an entity. However, on the @Insert annotation for insertPartial(), we use an entity property to teach Room what @Entity is associated with this @Insert operation:

    @Insert(entity = DefaultValueEntity::class)
    fun insertPartial(partial: PartialDefaultValue)

The POJO or Kotlin data class that you supply as input (PartialDataEntity) will be used like the entity itself, based on individual property names. Room will look in the supplied object for properties matching those of the entity class and use the ones that it finds. For everything else — such as text and version in this case — Room will rely on the underlying table either supporting NULL for the column… or having a defaultValue supplied.

Many developers will not need these features. Kotlin property default values and an ordinary @Insert will suffice. But, these features make it a bit easier to model classic SQL database structures and may help in some scenarios.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.