Room Relationships Explained

Michal Ankiersztajn
ProAndroidDev
Published in
3 min readJan 24, 2024

--

Relationships are essential concept in projecting and implementing your database. Learn how to define and use one-to-one, one-to-many and many-to-many relationships in Android Room.

For those who want to jump into the code, check the data package inside:

1. One-To-One Relationship

One-To-One
@Entity
data class UserEntity(
@PrimaryKey val id: Long,
val name: String,
val surname: String,
)

@Entity
data class LibraryEntity(
@PrimaryKey val id: Long,
val ownerId: Long,
)

Now that we have defined the entities, how do we query them together? You must create a new class that embeds the User and uses the relation between the Library and the User.

data class UserWithLibrary(
@Embedded val user: UserEntity,
@Relation(
parentColumn = "id",
entityColumn = "ownerId",
)
val library: LibraryEntity,
)

Then, in your DAO, you can query it together:

@Transaction
@Query("SELECT * FROM UserEntity")
suspend fun getUsersWithLibraries(): List<UserWithLibrary>

Querying a class with Relation fields requires you to use @Transaction.

2. One-To-Many Relationship

One-To-Many

Now, let’s add the Collections so the Users can create them inside their Library.

@Entity
data class CollectionEntity(
@PrimaryKey val id: Long,
val name: String,
val libraryId: Long,
)

Similar to a one-to-one relationship, we need to create a class with an @Relation, but its field will be a list.

data class LibraryWithCollections(
@Embedded val library: LibraryEntity,
@Relation(
parentColumn = "id",
entityColumn = "libraryId"
)
val collections: List<CollectionEntity>,
)

Then, in your DAO, query it together with libraries:

@Transaction
@Query("SELECT * FROM LibraryEntity")
suspend fun getLibrariesWithCollections(): List<LibraryWithCollections>

3. Many-To-Many Relationship

Now, we want to allow Users to add Games to their Collections and let other Users browse Collections containing the Game to find new exciting games. To make many-to-many relationships possible, we need a cross-reference table.

@Entity
data class GameEntity(
@PrimaryKey val id: Long,
val name: String,
)

@Entity
data class CollectionGameCrossRef(
@PrimaryKey val collectionId: Long,
@PrimaryKey val gameId: Long,
)

Now, there’ll be two classes using the relation:

data class GameWithCollections(
@Embedded val game: GameEntity,
@Relation(
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(CollectionGameCrossRef::class)
)
val collection: List<CollectionEntity>,
)

data class CollectionWithGames(
@Embedded val collection: CollectionEntity,
@Relation(
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(CollectionGameCrossRef::class)
)
val games: List<GameEntity>,
)

They’re very similar because we named the primary key inside GameEntity and CollectionEntity with the same name id. Then you’re able to query it inside your DAO:

@Transaction
@Query("SELECT * FROM CollectionEntity")
fun getCollectionsWithGames(): List<CollectionWithGames>

@Transaction
@Query("SELECT * FROM GameEntity")
fun getGamesWithCollections(): List<GameWithCollections>

Congratulations! Now you know how to define and use all possible relationships between data in Room! I’ll be grateful for a 👏 if you like it!

Check out the GitHub code:

References:

--

--