Room can convert primitive and boxed types, but doesn't allow object references
between entities. Learn how to use type converters and why Room doesn't support
object references.

## Use type converters

Sometimes, you need your app to store a custom data type in a single database
column. You support custom types by providing *type converters* . These are
functions that tell Room how to convert custom types to and from known types
that Room can persist. You identify type converters by using the
[`@ColumnTypeConverter`](https://developer.android.com/reference/kotlin/androidx/room3/ColumnTypeConverter) annotation.

> [!NOTE]
> **Note:** Room includes a default type converter for persisting enums. Existing type converters take precedence over the default. However, if you haven't defined a type converter for enums, you don't need to define one.

Suppose you need to persist instances of [`Date`](https://developer.android.com/reference/java/util/Date) in
your Room database. Room can't natively persist `Date` objects, so you need
to define type converters:


```kotlin
object Converters {
    @ColumnTypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }

    @ColumnTypeConverter
    fun dateToTimestamp(date: Date?): Long? {
        return date?.time
    }
}
```

<br />

This example defines two type converter functions: one that converts a `Date`
object to a `Long` object, and one that converts a `Long` object back to a
`Date` object. Because Room can persist `Long` objects, it can use these
converters to persist `Date` objects.

Next, you add the [`@ColumnTypeConverters`](https://developer.android.com/reference/kotlin/androidx/room3/ColumnTypeConverters)
annotation to the `AppDatabase` class so that Room can use the converter
class that you've defined:


```kotlin
@Database(entities = [User::class], version = 1)
@ColumnTypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
```

<br />

With these type converters defined, you can use your custom type in your
entities and DAOs just as you would use primitive types:


```kotlin
@Entity
data class User(
    @PrimaryKey val id: Long,
    val name: String,
    val birthday: Date?
)

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE birthday = :targetDate")
    suspend fun findUsersBornOnDate(targetDate: Date): List<User>
}
```

<br />

Because you annotated `AppDatabase` with `@ColumnTypeConverters` in this
example, Room can use the defined type converter everywhere. To scope type
converters to specific entities or DAOs instead, annotate your `@Entity` or
`@Dao` classes with `@ColumnTypeConverters`.

### Control type converter initialization

Ordinarily, Room instantiates type converters for you. However, if you need to
pass additional dependencies to your type converter classes, your app must
directly control their initialization. In that case, annotate your converter
class with [`@ProvidedColumnTypeConverter`](https://developer.android.com/reference/kotlin/androidx/room3/ProvidedColumnTypeConverter):


```kotlin
@ProvidedColumnTypeConverter
class ExampleConverter {
    @ColumnTypeConverter
    fun stringToExample(string: String?): ExampleType? {
        return string?.let { ExampleType() }
    }

    @ColumnTypeConverter
    fun exampleToString(example: ExampleType?): String? {
        return example?.toString()
    }
}
```

<br />

In addition to declaring your converter class in `@ColumnTypeConverters`, use
the [`RoomDatabase.Builder.addColumnTypeConverter`](https://developer.android.com/reference/kotlin/androidx/room3/RoomDatabase.Builder#addColumnTypeConverter(kotlin.Any)) function to pass an
instance of your converter class to the `RoomDatabase` builder:


```kotlin
val db = Room.databaseBuilder<MyDatabase>(applicationContext, "database-name")
    .addColumnTypeConverter(exampleConverterInstance)
    .build()
```

<br />

## Understand why Room doesn't allow object references

**Key takeaway:**
Room disallows object references between entity classes. Instead, you must
explicitly request the data that your app needs.

Mapping relationships from a database to the respective object model is a common
practice and works very well on the server side. Even when the program loads
properties as they're accessed, the server still performs well.

However, on the client side, this type of lazy loading isn't feasible because
it usually occurs on the UI thread, and querying information on disk in the UI
thread creates significant performance problems. The UI thread typically has
about 16 ms to calculate and draw an activity's updated layout, so even if a
query takes only 5 ms, it's still likely that your app will run out of time to
draw the frame, causing noticeable visual glitches. The query could take even
more time to complete if there's a separate transaction running in parallel, or
if the device is running other disk-intensive tasks. If you don't use lazy
loading, however, your app fetches more data than it needs, creating memory
consumption problems.

Object-relational mappings usually leave this decision to developers so that
they can do whatever is best for their app's use cases. Developers usually
decide to share the model between their app and the UI. This solution doesn't
scale well, however, because as the UI changes over time, the shared model
creates problems that are difficult for developers to anticipate and debug.

For example, consider a UI that loads a list of `Book` objects, with each book
having an `Author` object. You might initially design your queries to use lazy
loading to have instances of `Book` retrieve the author. The first retrieval
of the `author` property queries the database. Some time later, you realize
that you need to display the author name in your app's UI, as well. You can
access this name, as shown in the following code snippet:

    Text(text = book.author.name)

However, this seemingly innocent change causes the `Author` table to be queried
on the main thread.

If you query author information ahead of time but don't need it, it's difficult
to change how data is loaded. For example, if your app's UI no longer needs to
display `Author` information, your app effectively loads data that it doesn't
display, wasting valuable memory space. Your app's efficiency degrades even
further if the `Author` class references another table, such as `Books`.

To reference multiple entities at the same time using Room, create a data object
that contains each entity, and then write a query that joins the corresponding
tables. This well-structured model, combined with Room's robust
query validation capabilities, allows your app to consume fewer resources when
loading data, improving your app's performance and user experience.