Как сделать первичный ключ автоинкрементным в Room (Android/Kotlin)?
Правильный способ создания автоинкрементного первичного ключа в Room
Вы на верном пути, но есть несколько нюансов. В Room для автоинкрементного первичного ключа нужно использовать аннотацию @PrimaryKey с параметром autoGenerate = true. При этом тип поля должен быть Long (или Int, но Long предпочтительнее для больших таблиц).
Вот исправленный код вашей сущности:
@Entity(tableName = "notes")
data class Note(
@PrimaryKey(autoGenerate = true)
val id: Long = 0,
val title: String,
val content: String
)
Что изменилось и почему?
- Тип данных:
LongвместоInt. Room рекомендуетLongдля автоинкрементных ключей, так как это позволяет избежать переполнения при большом количестве записей. - Параметр
autoGenerate = true: Указывает Room автоматически генерировать уникальное значение при вставке. - Значение по умолчанию
0: При создании объекта Note без указания id (или с id = 0) Room проигнорирует это значение и сгенерирует новое.
Как это работает в DAO?
Ваш DAO-интерфейс может выглядеть так:
@Dao
interface NoteDao {
@Insert
suspend fun insert(note: Note): Long // Возвращает сгенерированный id
@Insert
suspend fun insertAll(notes: List<Note>): List<Long>
@Query("SELECT * FROM notes ORDER BY id DESC")
suspend fun getAllNotes(): List<Note>
}
Метод insert возвращает Long — это и есть сгенерированный автоинкрементный id. Если вы вставляете список, то возвращается список id.
Пример использования в ViewModel или Repository:
class NoteRepository(private val noteDao: NoteDao) {
suspend fun addNote(title: String, content: String): Long {
val note = Note(
title = title,
content = content
// id не указываем — будет 0, Room сгенерирует новый
)
return noteDao.insert(note)
}
}
Важные моменты:
- Не используйте
@NonNullс автоинкрементным ключом. Room сам управляет значением. - Если вы хотите использовать
IntвместоLong, это тоже возможно:@PrimaryKey(autoGenerate = true) val id: Int = 0. Но для безопасности используйтеLong. - Проверьте версию Room: Убедитесь, что у вас в
build.gradleподключена актуальная версия (например, 2.6.0+). В старых версиях могли быть баги.
Теперь при каждой вставке Room будет автоматически увеличивать id, начиная с 1. Ошибки с дублированием ключа больше не будет.
<<Альтернативный подход: использование Int и ручное управление
Если по каким-то причинам вы не хотите использовать Long, можно оставить Int с автоинкрементом. Но учтите, что максимальное значение Int — 2 147 483 647, что для большинства приложений более чем достаточно.
@Entity(tableName = "notes")
data class Note(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val title: String,
val content: String
)
Также можно использовать @ColumnInfo(name = "id") для явного указания имени столбца, если оно отличается от имени поля.
Дополнительный совет: отладка
Если Room всё равно выдает ошибку при вставке, проверьте:
- Не удалили ли вы базу данных вручную (например, через
fallbackToDestructiveMigration())? - Не пытаетесь ли вы вставить объект с явно указанным id, который уже существует? Room не будет перезаписывать записи, если не использовать аннотацию
@Upsert(доступна с Room 2.5.0+).
В большинстве случаев решение с Long и autoGenerate = true является стандартом для Room. Используйте его как основной подход.