SQLite Database
Purpose
Storing structured, repeatable data locally on Android devices.
Basic Operations
- Define a contract for database schema
- Create the database instance
- Insert new records
- Retrieve existing records
- Delete records
- Update existing records
- Maintain database connections and close them, typically in an Activity's
onDestroy()method
Limitations
The native android.database.sqlite API provides core database funcsionality but has critical drawbacks:
- No compile-time validation for raw SQL queries, leading to time-consuming runtime errors
- Requires extensive boilerplate code to convert SQL results and parameters between data objects and query syntax
Room Persistence Library
Purpose
Act as a robust abstraction layer on top of SQLite, combining SQLite's power with a clean, efficient API for local data persistence.
Key Advantages
- Compile-time SQL validation eliminates many runtime errors from query syntax issues
- Annotation-based API minimizes repetitive and error-prone boilerplate code
- Simplified migration support for managing database schema changes
Dependency Setup
Add the following Room dependencies to your build.gradle.kts file:
dependencies {
val roomVersion = "2.6.1"
// Core runtime library
implementation("androidx.room:room-runtime:$roomVersion")
// Kotlin Symbol Processing (KSP) compiler support
ksp("androidx.room:room-compiler:$roomVersion")
// Kotlin extensions and coroutine support
implementation("androidx.room:room-ktx:$roomVersion")
// Optional: RxJava3 support
implementation("androidx.room:room-rxjava3:$roomVersion")
// Optional: Guava support (ListenableFuture, Optional)
implementation("androidx.room:room-guava:$roomVersion")
// Optional: Test helpers
testImplementation("androidx.room:room-testing:$roomVersion")
// Optional: Paging 3 integration
implementation("androidx.room:room-paging:$roomVersion")
}
Core Components
- Database class: Encapsulates the database instance and serves as the main entry point for external access
- Entities: Annotated data classes representing database tables
- Data Access Objects (DAOs): Interfaces with annotated methods defining all database operations (CRUD)
Usage Steps
Step 1: Define a Data Entity
Create an annotated data class to represent a database table:
@Entity
data class Contact(
@PrimaryKey val id: Long,
@ColumnInfo(name = "given_name") val givenName: String?,
@ColumnInfo(name = "family_name") val familyName: String?
)
Step 2: Create a DAO Interface
Define a DAO interface to declare all database operations:
@Dao
interface ContactDao {
@Query("SELECT * FROM contact")
fun getAllContacts(): List<Contact>
@Query("SELECT * FROM contact WHERE id IN (:contactIds)")
fun getContactsByIds(contactIds: LongArray): List<Contact>
@Query(
"SELECT * FROM contact WHERE given_name LIKE :first AND " +
"family_name LIKE :last LIMIT 1"
)
fun findContactByNames(first: String, last: String): Contact
@Insert
fun insertContacts(vararg contacts: Contact)
@Delete
fun deleteContact(contact: Contact)
}
Step 3: Define the Database Class
Create an abstract class extending RoomDatabase to manage the database instance:
@Database(entities = [Contact::class], version = 1)
abstract class LocalDatabase : RoomDatabase() {
abstract fun contactDao(): ContactDao
}
Step 4: Initialize and Use the Databace
Instantiate the database and access operations through the DAO:
val localDb = Room.databaseBuilder(
applicationContext,
LocalDatabase::class.java,
"local-contacts-db"
).build()
val contactDao = localDb.contactDao()
val allContacts: List<Contact> = contactDao.getAllContacts()