DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Room Relations Guide - One-to-Many, Many-to-Many, and Embedded in Android

Room Relations Guide - One-to-Many, Many-to-Many, and Embedded in Android

Android Room database relationships can be complex, but with the right patterns, you can create well-structured data models. This guide covers the main relationship patterns you'll encounter.

@Embedded - One-to-One Relationships

Embed related data in a single entity:

@Entity(tableName = "addresses")
data class Address(
    val street: String,
    val city: String,
    val zipCode: String
)

@Entity(tableName = "users")
data class User(
    @PrimaryKey val id: Int,
    val name: String,
    @Embedded val address: Address
)

// Query - address fields are flattened in users table
@Dao
interface UserDao {
    @Query("SELECT * FROM users WHERE id = :id")
    suspend fun getUserWithAddress(id: Int): User
}
Enter fullscreen mode Exit fullscreen mode

@Relation - One-to-Many Relationships

Link parent entity with multiple child entities:

@Entity(tableName = "authors")
data class Author(
    @PrimaryKey val id: Int,
    val name: String
)

@Entity(tableName = "books")
data class Book(
    @PrimaryKey val id: Int,
    val title: String,
    val authorId: Int,
    @ForeignKey(entity = Author::class, parentColumns = ["id"], childColumns = ["authorId"])
)

data class AuthorWithBooks(
    @Embedded val author: Author,
    @Relation(parentColumn = "id", entityColumn = "authorId")
    val books: List<Book>
)

@Dao
interface AuthorDao {
    @Transaction
    @Query("SELECT * FROM authors")
    suspend fun getAuthorsWithBooks(): List<AuthorWithBooks>
}
Enter fullscreen mode Exit fullscreen mode

Junction Tables - Many-to-Many Relationships

Connect two entities through a junction table:

@Entity(tableName = "students")
data class Student(
    @PrimaryKey val id: Int,
    val name: String
)

@Entity(tableName = "courses")
data class Course(
    @PrimaryKey val id: Int,
    val title: String
)

@Entity(
    tableName = "student_course",
    primaryKeys = ["studentId", "courseId"],
    foreignKeys = [
        ForeignKey(Student::class, ["id"], ["studentId"]),
        ForeignKey(Course::class, ["id"], ["courseId"])
    ]
)
data class StudentCourse(
    val studentId: Int,
    val courseId: Int
)

data class StudentWithCourses(
    @Embedded val student: Student,
    @Relation(
        parentColumn = "id",
        entityColumn = "id",
        associateBy = Junction(StudentCourse::class, parentColumn = "studentId", entityColumn = "courseId")
    )
    val courses: List<Course>
)

@Dao
interface StudentDao {
    @Transaction
    @Query("SELECT * FROM students")
    suspend fun getStudentsWithCourses(): List<StudentWithCourses>
}
Enter fullscreen mode Exit fullscreen mode

Type Converters - Custom Data Types

Convert complex types to Room-compatible formats:

data class Timestamp(val millis: Long)

class Converters {
    @TypeConverter
    fun timestampToLong(timestamp: Timestamp?): Long? = timestamp?.millis

    @TypeConverter
    fun longToTimestamp(millis: Long?): Timestamp? =
        millis?.let { Timestamp(it) }

    @TypeConverter
    fun listToJson(list: List<String>): String =
        Json.encodeToString(list)

    @TypeConverter
    fun jsonToList(json: String): List<String> =
        Json.decodeFromString(json)
}

@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
Enter fullscreen mode Exit fullscreen mode

@Transaction - Data Consistency

Ensure related operations complete atomically:

@Dao
interface TransactionDao {
    @Transaction
    suspend fun insertUserAndAddress(user: User, address: Address) {
        insertUser(user)
        insertAddress(address)
    }

    @Transaction
    @Query("SELECT * FROM users WHERE id = :id")
    suspend fun getUserWithAllRelations(id: Int): UserData
}
Enter fullscreen mode Exit fullscreen mode

Relationship Patterns Summary

Pattern Structure Use Case
@Embedded 1:1 in same table Address, contact info
@Relation 1:many across tables Author → Books
Junction Many:many with link table Students ↔ Courses
@TypeConverter Custom type mapping Enums, complex objects
@Transaction Atomic operations Multi-entity inserts

8 Android app templates available on Gumroad

Top comments (0)