Skip to content

Android Room Entity — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about Android Room Entity. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Your app crashes on first launch because Room can't create the table — missing primary keys, wrong column types, or schema mismatch after Migration.

Wrong Approach ❌

@Entity(tableName = "users")
data class User(
    val id: String, // No @PrimaryKey annotation
    val name: String,
    @ColumnInfo(name = "created_at")
    val createdAt: Date, // Room doesn't know how to store Date!
    val tags: List<String> // List not supported by default
)

Output: SQLiteException: PRIMARY KEY missing or Cannot figure out how to save this field into database.

Right Approach ✅

@Entity(tableName = "users")
data class User(
    @PrimaryKey
    val id: String,
    @ColumnInfo(name = "full_name")
    val name: String,
    @ColumnInfo(name = "created_at")
    @TypeConverters(DateConverter::class)
    val createdAt: Long, // Store as timestamp
    @Ignore // Not stored in database
    val tags: List<String> = emptyList()
)
class DateConverter {
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? = value?.let { Date(it) }

    @TypeConverter
    fun dateToTimestamp(date: Date?): Long? = date?.time
}

Output: Properly defined entity with correct types and converters.

Prevention

  • Always annotate exactly one field with @PrimaryKey (or use @PrimaryKey(autoGenerate = true)).
  • Use @Ignore for fields that should not be persisted.
  • Add @TypeConverters at the field or class level for custom types.
  • Prefer primitive types (Long, String, Int) over Date, List, etc.
  • Set foreignKeys carefully — they can break migrations.

Common Mistakes with room entity

  1. Using return to exit a function early instead of wrapping a pure value in the monad
  2. Mixing let bindings with <- bindings in do notation, producing type errors
  3. Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors

These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Can I have a Composite primary key?

Yes. Use @PrimaryKey(primaryKeys = ["userId", "groupId"]) on the entity class. Room creates a multi-column primary key constraint.

### What happens if I add a new field to an existing entity?

Room throws MigrationNeededException unless you provide a Migration or set fallbackToDestructiveMigration(). Always write a Migration when changing your schema.

### What is the difference between @Ignore and @Transient?

@Ignore is Room-specific and excludes the field from persistence and constructor params. @Transient is a Java annotation that also works with serialisation. Prefer @Ignore in Room entities.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro