← Назад к вопросам

Как связать две таблицы в Room

1.3 Junior🔥 121 комментариев
#Android компоненты#Работа с данными

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как связать две таблицы в Room

Room — это ORM (Object-Relational Mapping) библиотека для Android, встроенная в AndroidX. Связывание таблиц осуществляется через внешние ключи (foreign keys) и специальные аннотации.

1. Определение структуры с внешним ключом

Сначала создадим две сущности (entities) с отношением один-ко-многим (User → Post):

// Таблица пользователей
@Entity(tableName = "users")
data class User(
    @PrimaryKey val id: Long,
    val name: String,
    val email: String
)

// Таблица постов с внешним ключом на User
@Entity(
    tableName = "posts",
    foreignKeys = [
        ForeignKey(
            entity = User::class,
            parentColumns = ["id"],
            childColumns = ["userId"],
            onDelete = ForeignKey.CASCADE,  // При удалении юзера удалить посты
            onUpdate = ForeignKey.CASCADE
        )
    ],
    indices = [
        Index("userId")  // Индекс для оптимизации запросов
    ]
)
data class Post(
    @PrimaryKey val id: Long,
    val userId: Long,  // Внешний ключ
    val title: String,
    val content: String
)

2. Создание класса для связи данных

Для загрузки связанных данных используется специальный класс POJO:

data class UserWithPosts(
    @Embedded
    val user: User,
    
    @Relation(
        parentColumn = "id",
        entityColumn = "userId"
    )
    val posts: List<Post>
)

3. Создание DAO (Data Access Object)

DAO содержит методы для работы с данными:

@Dao
interface UserPostDao {
    
    // Получить пользователя со всеми его постами
    @Transaction
    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserWithPosts(userId: Long): UserWithPosts
    
    // Получить все пользователей со их постами
    @Transaction
    @Query("SELECT * FROM users")
    suspend fun getAllUsersWithPosts(): List<UserWithPosts>
    
    // Вставить пользователя
    @Insert
    suspend fun insertUser(user: User)
    
    // Вставить пост
    @Insert
    suspend fun insertPost(post: Post)
    
    // Обновить пост
    @Update
    suspend fun updatePost(post: Post)
    
    // Удалить пост
    @Delete
    suspend fun deletePost(post: Post)
}

4. Создание Database класса

@Database(
    entities = [User::class, Post::class],
    version = 1,
    exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userPostDao(): UserPostDao
    
    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null
        
        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                ).build().also { INSTANCE = it }
            }
        }
    }
}

5. Использование в коде

class UserRepository(private val dao: UserPostDao) {
    
    suspend fun loadUserWithPosts(userId: Long): UserWithPosts {
        return dao.getUserWithPosts(userId)
    }
    
    suspend fun addPost(userId: Long, title: String, content: String) {
        val post = Post(
            id = System.currentTimeMillis(),
            userId = userId,
            title = title,
            content = content
        )
        dao.insertPost(post)
    }
}

6. Типы отношений в Room

One-to-One (Один-к-одному):

data class UserWithProfile(
    @Embedded val user: User,
    @Relation(parentColumn = "id", entityColumn = "userId")
    val profile: Profile
)

One-to-Many (Один-ко-многим): — как показано выше

Many-to-Many (Много-ко-многим):

// Связующая таблица
@Entity(
    tableName = "user_book_junction",
    primaryKeys = ["userId", "bookId"],
    foreignKeys = [
        ForeignKey(entity = User::class, parentColumns = ["id"], childColumns = ["userId"]),
        ForeignKey(entity = Book::class, parentColumns = ["id"], childColumns = ["bookId"])
    ]
)
data class UserBook(val userId: Long, val bookId: Long)

data class UserWithBooks(
    @Embedded val user: User,
    @Relation(
        parentColumn = "id",
        entityColumn = "id",
        associateBy = Junction(UserBook::class, parentColumn = "userId", entityColumn = "bookId")
    )
    val books: List<Book>
)

Важные моменты

  • @Transaction гарантирует, что запрос и загрузка связей выполнятся в одной транзакции
  • ForeignKey.CASCADE автоматически удаляет подчиненные записи
  • @Embedded встраивает поля сущности в родительский класс
  • Indices улучшают производительность запросов
  • Room работает асинхронно (suspend функции)

Это стандартный способ работы со связанными данными в Android.

Как связать две таблицы в Room | PrepBro