Question
I am creating a Room Persistence Library Entity class named Food, and I want foodId to behave as an auto-incrementing primary key.
@Entity
class Food(
var foodName: String,
var foodDesc: String,
var protein: Double,
var carbs: Double,
var fat: Double
) {
@PrimaryKey
var foodId: Int = 0
var calories: Double = 0.0
}
How can I make foodId an auto-increment field in Room?
Short Answer
By the end of this page, you will understand how Room handles primary keys, how to enable automatic ID generation with @PrimaryKey(autoGenerate = true), how inserts behave, and how to avoid common mistakes when designing Room entities in Kotlin.
Concept
Room is Android's ORM layer on top of SQLite. When you define an @Entity, Room maps that Kotlin class to a database table.
A primary key is a column whose value uniquely identifies each row. In many apps, you do not want to manually assign this value every time you insert data. Instead, you want the database to generate a new unique ID automatically.
In Room, this is done with:
@PrimaryKey(autoGenerate = true)
When autoGenerate = true is enabled:
- Room treats the field as a generated primary key.
- You usually pass
0for anIntkey when inserting a new object. - SQLite generates the actual ID value for the row.
- Room can return the inserted row ID from the DAO.
This matters because auto-generated IDs are very common in real apps:
- users
- products
- notes
- meals
- messages
Without auto-generation, you would need to manually manage unique IDs, which is error-prone and unnecessary in most local database use cases.
Mental Model
Think of a Room table like a notebook where each row needs a unique ticket number.
- The row data is the actual note you write.
- The primary key is the ticket number.
autoGenerate = truemeans the notebook assigns the next ticket number automatically.
So instead of saying, "This food must have ID 17," you say, "Please add this food," and Room gives it the next available ID.
Syntax and Examples
The key syntax is:
@PrimaryKey(autoGenerate = true)
var foodId: Int = 0
A common Room entity looks like this:
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity
data class Food(
val foodName: String,
val foodDesc: String,
val protein: Double,
val carbs: Double,
val fat: Double,
val calories: Double = 0.0,
@PrimaryKey(autoGenerate = true)
val foodId: Int = 0
)
Why 0?
For an Int primary key with auto-generation, 0 is commonly used as the default value for new objects. Room understands that this row does not yet have a real ID from the database.
DAO example
Step by Step Execution
Consider this entity and insert call:
@Entity
data class Food(
val foodName: String,
val foodDesc: String,
val protein: Double,
val carbs: Double,
val fat: Double,
@PrimaryKey(autoGenerate = true)
val foodId: Int = 0
)
val food = Food(
foodName = "Egg",
foodDesc = "Boiled egg",
protein = 6.0,
carbs = 0.6,
fat = 5.0
)
val id = foodDao.insert(food)
What happens step by step:
- A
Foodobject is created. foodIdis left at its default value of0.foodDao.insert(food)sends the object to Room.- Room sees that
foodIdis the primary key and hasautoGenerate = true.
Real World Use Cases
Auto-generated primary keys are useful in many real applications:
- Food tracking apps: each meal or food item gets a unique local ID.
- Notes apps: every note needs a stable identifier.
- Offline-first apps: items created locally need unique IDs before syncing.
- Inventory apps: each product record needs a distinct key.
- Task managers: every task should be uniquely identifiable for updates and deletes.
For example, if you want to edit or delete a food item later, the generated foodId gives you a reliable way to find that exact row.
Real Codebase Usage
In real projects, developers often combine auto-generated IDs with a few common patterns.
1. Insert and get the generated ID
@Insert
suspend fun insert(food: Food): Long
This is useful when you need to immediately navigate to a detail screen or save related data.
2. Use the ID for updates and deletes
@Update
suspend fun update(food: Food)
@Delete
suspend fun delete(food: Food)
The primary key helps Room know which row to update.
3. Keep entity creation simple
Developers usually create new entities without manually assigning IDs:
val food = Food("Apple", "Fresh apple", 0.3, 14.0, 0.2)
4. Validation before insert
Common Mistakes
1. Forgetting autoGenerate = true
Broken example:
@PrimaryKey
var foodId: Int = 0
Problem:
- Room treats
foodIdas a normal primary key. - If you keep inserting rows with
0, you can get conflicts.
Fix:
@PrimaryKey(autoGenerate = true)
var foodId: Int = 0
2. Manually setting IDs for new rows unnecessarily
Broken example:
val food = Food("Milk", "Low fat milk", 3.4, 5.0, 1.0, foodId = 99)
Problem:
- You may create collisions with existing rows.
- It defeats the purpose of auto-generation.
Fix:
- Let Room assign the ID unless you have a specific reason not to.
3. Expecting the object to automatically update its property after insert
Comparisons
| Concept | What it does | When to use |
|---|---|---|
@PrimaryKey | Marks a field as the unique row identifier | Use when you will provide the ID yourself |
@PrimaryKey(autoGenerate = true) | Marks a field as the primary key and lets Room/SQLite generate it | Use for most local entities |
| Manual ID assignment | You set the value before insert | Use only when IDs come from another source |
| Auto-generated local ID | Database creates the value | Best for offline/local records |
Int vs Long for Room primary keys
| Type | Example | Notes |
|---|
Cheat Sheet
@PrimaryKey(autoGenerate = true)
val id: Int = 0
Rules
- Add
autoGenerate = trueto the primary key annotation. - Use
0forIntor0LforLongas the default value. - Do not manually assign IDs for normal inserts.
- Use the DAO
@Insertreturn value to get the generated row ID.
Example entity
@Entity
data class Food(
val foodName: String,
val foodDesc: String,
val protein: Double,
val carbs: Double,
val fat: Double,
val calories: Double = 0.0,
@PrimaryKey(autoGenerate = true)
val foodId: Int = 0
)
Example DAO
FAQ
How do I make a Room primary key auto-increment?
Use @PrimaryKey(autoGenerate = true) on the ID field.
Should I use Int or Long for a Room primary key?
Both work, but Long is often preferred for safety in larger databases.
What default value should I use for an auto-generated ID in Room?
Use 0 for Int or 0L for Long.
Do I need to set the ID before inserting a row?
No. For auto-generated keys, let Room and SQLite create it.
Can I still update a row if the ID is auto-generated?
Yes. Once the row exists, its generated primary key identifies it for updates and deletes.
Does Room automatically return the generated ID after insert?
It can, if your @Insert DAO method returns Long or List<Long>.
Is auto-increment the same as a unique key?
Not exactly. A primary key must be unique. Auto-generation is just a way to create that unique value automatically.
Mini Project
Description
Build a simple food storage feature for an Android app using Room. Each food item should be saved with a name and nutrition values, while the database automatically assigns a unique ID. This demonstrates how auto-generated primary keys work in a practical setup.
Goal
Create a Room entity and DAO that can insert food items and return the generated ID without manually setting the primary key.
Requirements
- Create a
FoodRoom entity with an auto-generated primary key. - Add fields for name, description, protein, carbs, fat, and calories.
- Create a DAO with an insert method that returns the generated ID.
- Insert at least one
Foodobject without manually setting its ID.
Keep learning
Related questions
Android AlarmManager Example: Scheduling Tasks with AlarmManager
Learn how to use Android AlarmManager to schedule tasks, set alarms, and handle broadcasts with a simple beginner example.
Can You Extend a Data Class in Kotlin? Inheritance, Limits, and Better Alternatives
Learn why Kotlin data classes cannot be extended, what causes the component function clash, and which alternatives to use instead.
Difference Between List and Array in Kotlin
Learn the difference between List and Array in Kotlin, including mutability, size, APIs, performance, and when to use each one.