{"id":1719,"date":"2025-01-10T06:58:25","date_gmt":"2025-01-10T06:58:25","guid":{"rendered":"https:\/\/200oksolutions.com\/blog\/?p=1719"},"modified":"2026-10-21T06:43:46","modified_gmt":"2026-10-21T06:43:46","slug":"exploring-android-room-database-with-kotlin","status":"publish","type":"post","link":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin\/","title":{"rendered":"Exploring the Android Room Database with Kotlin"},"content":{"rendered":"\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe title=\"Exploring the Android Room Database with Kotlin\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/nHGIlgdeugM?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Introduction<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The Android Room database is Google&#8217;s recommended way to store structured data locally in an Android app. Room sits on top of SQLite and gives you compile-time checked queries, Kotlin coroutine and Flow support, and far less boilerplate than the raw SQLite APIs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide builds a small offline-first booking list with Room and Kotlin. You will set up dependencies with KSP, define an entity, a DAO and a database, expose data with Flow, handle schema changes with migrations, model relationships, and test everything without a device. Every code block here is meant to compile as written. If you spot a version difference, check the official Room release notes for the current stable version.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>What you will learn<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>How to set up Room with KSP<\/li>\n\n\n\n<li>How to write entities, DAOs and the database class<\/li>\n\n\n\n<li>Why Flow is usually the better choice than LiveData for new code<\/li>\n\n\n\n<li>How to write migrations that do not wipe user data<\/li>\n\n\n\n<li>How to model one-to-many relationships<\/li>\n\n\n\n<li>How to test your DAO with an in-memory database<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What is the Android Room database?<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Room is part of Android Jetpack. It has three main parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Entity:<\/strong> a Kotlin class that maps to a table.<\/li>\n\n\n\n<li><strong>DAO (Data Access Object):<\/strong> an interface where you declare your queries and write operations.<\/li>\n\n\n\n<li><strong>Database:<\/strong> an abstract class that ties entities and DAOs together and gives you the database instance.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Room checks your SQL at compile time, so a typo in a column name fails the build instead of crashing your app in production.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Setting up Room with Kotlin and KSP<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use KSP, not <code>annotationProcessor<\/code>, in Kotlin projects. Add the KSP plugin to your module, then the dependencies:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ build.gradle.kts (app)\nplugins {\n    id(\"com.android.application\")\n    id(\"org.jetbrains.kotlin.android\")\n    id(\"com.google.devtools.ksp\")\n}\n\ndependencies {\n    val roomVersion = \"2.6.1\" \/\/ check the latest stable release\n\n    implementation(\"androidx.room:room-runtime:$roomVersion\")\n    implementation(\"androidx.room:room-ktx:$roomVersion\")\n    ksp(\"androidx.room:room-compiler:$roomVersion\")\n\n    \/\/ Lifecycle libraries have their own versions, not Room's\n    implementation(\"androidx.lifecycle:lifecycle-viewmodel-ktx:&lt;lifecycle_version&gt;\")\n    implementation(\"androidx.lifecycle:lifecycle-runtime-ktx:&lt;lifecycle_version&gt;\")\n\n    testImplementation(\"androidx.room:room-testing:$roomVersion\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sync the project after adding these. One common mistake in older tutorials is reusing the Room version number for the lifecycle libraries. They are separate libraries with separate versions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step 1: Define an entity<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An entity is a table. Each property is a column.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity(tableName = \"bookings\")\ndata class Booking(\n    @PrimaryKey(autoGenerate = true) val id: Long = 0,\n    val guestName: String,\n    val roomNumber: Int,\n    val checkInEpochDay: Long\n)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>id<\/code> is auto-generated on insert. Dates are stored as an epoch-day <code>Long<\/code>, which is simple and sorts correctly. If you prefer <code>LocalDate<\/code>, see the TypeConverters section below.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step 2: Create the DAO<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The DAO declares how you read and write data. Write operations are <code>suspend<\/code> functions, and reads return <code>Flow<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import androidx.room.*\nimport kotlinx.coroutines.flow.Flow\n\n@Dao\ninterface BookingDao {\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insert(booking: Booking): Long\n\n    @Update\n    suspend fun update(booking: Booking)\n\n    @Delete\n    suspend fun delete(booking: Booking)\n\n    @Query(\"SELECT * FROM bookings ORDER BY checkInEpochDay ASC\")\n    fun observeAll(): Flow&lt;List&lt;Booking&gt;&gt;\n\n    @Query(\"SELECT * FROM bookings WHERE id = :id\")\n    suspend fun getById(id: Long): Booking?\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Room runs suspend DAO functions off the main thread for you, so you do not need <code>withContext(Dispatchers.IO)<\/code> around them. The Flow re-emits automatically whenever the <code>bookings<\/code> table changes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step 3: Create the database<\/strong><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>import android.content.Context\nimport androidx.room.Database\nimport androidx.room.Room\nimport androidx.room.RoomDatabase\n\n@Database(entities = &#91;Booking::class], version = 1, exportSchema = true)\nabstract class AppDatabase : RoomDatabase() {\n\n    abstract fun bookingDao(): BookingDao\n\n    companion object {\n        @Volatile private var INSTANCE: AppDatabase? = null\n\n        fun getInstance(context: Context): AppDatabase =\n            INSTANCE ?: synchronized(this) {\n                INSTANCE ?: Room.databaseBuilder(\n                    context.applicationContext,\n                    AppDatabase::class.java,\n                    \"app.db\"\n                ).build().also { INSTANCE = it }\n            }\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Set <code>exportSchema = true<\/code> and configure a schema directory in Gradle. Exported schema JSON files are what let you write and test migrations later. Many tutorials set it to <code>false<\/code> and then have no safe path for schema changes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you use Hilt, provide the database and DAO from a module instead of the singleton above.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step 4: Repository and ViewModel<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The repository keeps your UI independent of Room.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class BookingRepository(private val dao: BookingDao) {\n    val bookings: Flow&lt;List&lt;Booking&gt;&gt; = dao.observeAll()\n\n    suspend fun add(booking: Booking) = dao.insert(booking)\n    suspend fun remove(booking: Booking) = dao.delete(booking)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The ViewModel turns the Flow into state the UI can collect, using <code>viewModelScope<\/code>, which is cancelled automatically when the ViewModel is cleared:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import android.app.Application\nimport androidx.lifecycle.AndroidViewModel\nimport androidx.lifecycle.viewModelScope\nimport kotlinx.coroutines.flow.*\nimport kotlinx.coroutines.launch\n\nclass BookingViewModel(app: Application) : AndroidViewModel(app) {\n\n    private val repository =\n        BookingRepository(AppDatabase.getInstance(app).bookingDao())\n\n    val bookings: StateFlow&lt;List&lt;Booking&gt;&gt; = repository.bookings\n        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())\n\n    fun add(guestName: String, roomNumber: Int, checkInEpochDay: Long) {\n        viewModelScope.launch {\n            repository.add(Booking(guestName = guestName, roomNumber = roomNumber, checkInEpochDay = checkInEpochDay))\n        }\n    }\n\n    fun remove(booking: Booking) {\n        viewModelScope.launch { repository.remove(booking) }\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step 5: Collect the data in your UI<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use ViewBinding, since <code>kotlinx.android.synthetic<\/code> has been removed, and collect the Flow in a lifecycle-aware way:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class MainActivity : AppCompatActivity() {\n\n    private lateinit var binding: ActivityMainBinding\n    private val viewModel: BookingViewModel by viewModels()\n    private val adapter = BookingAdapter() \/\/ your ListAdapter&lt;Booking, ...&gt;\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        binding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        binding.recyclerView.adapter = adapter\n\n        lifecycleScope.launch {\n            repeatOnLifecycle(Lifecycle.State.STARTED) {\n                viewModel.bookings.collect { adapter.submitList(it) }\n            }\n        }\n\n        binding.buttonAdd.setOnClickListener {\n            viewModel.add(\"John Doe\", 101, LocalDate.now().toEpochDay())\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In Jetpack Compose, replace the collection block with <code>viewModel.bookings.collectAsStateWithLifecycle()<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Room with Flow vs LiveData<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Room supports both. For new code, Flow is usually the better default:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th><\/th><th>Flow<\/th><th>LiveData<\/th><\/tr><tr><td>Works outside Android UI code (repositories, tests)<\/td><td>Yes<\/td><td>Awkward<\/td><\/tr><tr><td>Operators (map, combine, debounce)<\/td><td>Rich<\/td><td>Limited<\/td><\/tr><tr><td>Lifecycle awareness<\/td><td>Via <code>repeatOnLifecycle<\/code> or <code>collectAsStateWithLifecycle<\/code><\/td><td>Built in<\/td><\/tr><tr><td>Compose support<\/td><td>First-class<\/td><td>Needs an adapter<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">If your project already uses LiveData everywhere, there is no urgent reason to rewrite it. Mixing both in one screen is where problems start.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>TypeConverters<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Room stores primitives and strings. For other types, add a converter:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class Converters {\n    @TypeConverter fun fromLocalDate(value: LocalDate?): Long? = value?.toEpochDay()\n    @TypeConverter fun toLocalDate(value: Long?): LocalDate? = value?.let(LocalDate::ofEpochDay)\n}\n\n@Database(entities = &#91;Booking::class], version = 1, exportSchema = true)\n@TypeConverters(Converters::class)\nabstract class AppDatabase : RoomDatabase() { \/* ... *\/ }<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>One-to-many relations<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Say each guest has many bookings. Store <code>guestId<\/code> on the booking, then describe the relation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Entity(tableName = \"guests\")\ndata class Guest(\n    @PrimaryKey(autoGenerate = true) val id: Long = 0,\n    val name: String\n)\n\ndata class GuestWithBookings(\n    @Embedded val guest: Guest,\n    @Relation(parentColumn = \"id\", entityColumn = \"guestId\")\n    val bookings: List&lt;Booking&gt;\n)\n\n@Dao\ninterface GuestDao {\n    @Transaction\n    @Query(\"SELECT * FROM guests\")\n    fun observeGuestsWithBookings(): Flow&lt;List&lt;GuestWithBookings&gt;&gt;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>@Transaction<\/code> annotation matters. Room runs two queries for a relation, and the transaction keeps the result consistent. Add <code>@Index<\/code> on <code>guestId<\/code> so lookups stay fast as the table grows.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Database migrations<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is where most real apps get hurt. If you change an entity and bump the version without a migration, Room throws an exception at startup, or you wipe user data if you used <code>fallbackToDestructiveMigration()<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose version 2 adds a nullable <code>notes<\/code> column:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>val MIGRATION_1_2 = object : Migration(1, 2) {\n    override fun migrate(db: SupportSQLiteDatabase) {\n        db.execSQL(\"ALTER TABLE bookings ADD COLUMN notes TEXT\")\n    }\n}\n\nRoom.databaseBuilder(context, AppDatabase::class.java, \"app.db\")\n    .addMigrations(MIGRATION_1_2)\n    .build()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The entity gets <code>val notes: String? = null<\/code> and the database version becomes 2. For simple changes such as adding a column, Room also supports auto-migrations with <code>@Database(autoMigrations = [...])<\/code>. Both approaches depend on the exported schema files from the database step.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Rule of thumb:<\/strong> never ship <code>fallbackToDestructiveMigration()<\/code> in a production app that holds user data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Testing your DAO<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use an in-memory database so tests are fast and leave nothing behind:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@RunWith(AndroidJUnit4::class)\nclass BookingDaoTest {\n\n    private lateinit var db: AppDatabase\n    private lateinit var dao: BookingDao\n\n    @Before fun setUp() {\n        db = Room.inMemoryDatabaseBuilder(\n            ApplicationProvider.getApplicationContext(), AppDatabase::class.java\n        ).build()\n        dao = db.bookingDao()\n    }\n\n    @After fun tearDown() = db.close()\n\n    @Test fun insertAndRead() = runTest {\n        dao.insert(Booking(guestName = \"Asha\", roomNumber = 12, checkInEpochDay = 20000))\n        val all = dao.observeAll().first()\n        assertEquals(1, all.size)\n        assertEquals(\"Asha\", all.first().guestName)\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Room also provides <code>MigrationTestHelper<\/code> in <code>room-testing<\/code> to verify that a migration produces the schema you expect.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Common Room mistakes<\/strong><\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Querying on the main thread.<\/strong> Use suspend functions or Flow. Do not use <code>allowMainThreadQueries()<\/code> outside quick experiments.<\/li>\n\n\n\n<li><strong>Destructive migrations in production.<\/strong> Users lose their data on update.<\/li>\n\n\n\n<li><strong>Setting <code>exportSchema = false<\/code>.<\/strong> You lose the ability to test and automate migrations.<\/li>\n\n\n\n<li><strong>Using Room&#8217;s version number for other Jetpack libraries.<\/strong> They are versioned separately.<\/li>\n\n\n\n<li><strong>Missing indexes on foreign keys.<\/strong> Queries slow down as tables grow.<\/li>\n\n\n\n<li><strong>Creating a new database instance per screen.<\/strong> Use one instance per app, through a singleton or dependency injection.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Room vs SQLite vs DataStore<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Need<\/th><th>Best fit<\/th><\/tr><tr><td>Structured, queryable, relational data<\/td><td>Room<\/td><\/tr><tr><td>Full control of raw SQL, or an existing SQLite setup<\/td><td>SQLite directly, or Room on top of it<\/td><\/tr><tr><td>A few settings or flags<\/td><td>DataStore<\/td><\/tr><tr><td>Shared data across Kotlin Multiplatform targets<\/td><td>Check Room&#8217;s current multiplatform support in the release notes<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Frequently asked questions<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Q. Is Room better than raw SQLite on Android?<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> For most apps, yes. Room validates queries at compile time, removes boilerplate and integrates with coroutines and Flow. Raw SQLite is still useful when you need full control.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Q. Should I use Flow or LiveData with Room?<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> Flow is the better default for new code. LiveData is fine if your project is already built around it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Q. How do I migrate a Room database without losing data?<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> Bump the database version, write a <code>Migration<\/code> (or use an auto-migration), keep exported schemas, and test with <code>MigrationTestHelper<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Q. Does Room work with Jetpack Compose? <\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> Yes. Expose a Flow from your DAO, convert it to <code>StateFlow<\/code> in the ViewModel, and call <code>collectAsStateWithLifecycle()<\/code>  in your composable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Q. Can Room store lists or custom objects? <\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> Not directly. Use a <code>@TypeConverter<\/code>, or better, model them as a separate table with a relation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Build it right the first time<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Planning an Android app that has to work offline and survive every update? Your local data model is one of the hardest things to change once users have installed the app, so it pays to get the schema and migration plan right in version one.<\/p>\n\n\n<div class=\"is-default-size wp-block-site-logo\"><a href=\"https:\/\/www.200oksolutions.com\/blog\/\" class=\"custom-logo-link light-mode-logo\" rel=\"home\"><img fetchpriority=\"high\" decoding=\"async\" width=\"484\" height=\"191\" src=\"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2026\/01\/cropped-200ok_logo.png\" class=\"custom-logo\" alt=\"200OK Solutions Blog\" srcset=\"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2026\/01\/cropped-200ok_logo.png 484w, https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2026\/01\/cropped-200ok_logo-300x118.png 300w\" sizes=\"(max-width: 484px) 100vw, 484px\" \/><\/a><\/div>","protected":false},"excerpt":{"rendered":"<p>Introduction The Android Room database is Google&#8217;s recommended way to store structured data locally in&hellip;<\/p>\n","protected":false},"author":5,"featured_media":5284,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[115,7],"tags":[569,564,566,567,565,563,568],"class_list":["post-1719","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","category-mobile","tag-android-architecture-components","tag-android-development","tag-android-persistence-library","tag-data-persistence","tag-kotlin-room","tag-room-database","tag-sqlite-in-android"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Exploring the Android Room Database with Kotlin<\/title>\n<meta name=\"description\" content=\"Learn the Android Room database with Kotlin: setup with KSP, DAOs with Flow, migrations, relations and testing, with code that compiles.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Exploring the Android Room Database with Kotlin\" \/>\n<meta property=\"og:description\" content=\"Learn the Android Room database with Kotlin: setup with KSP, DAOs with Flow, migrations, relations and testing, with code that compiles.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin\/\" \/>\n<meta property=\"og:site_name\" content=\"200OK Solutions Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-01-10T06:58:25+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-21T06:43:46+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2025\/01\/How-to-Migrate-to-Cloud-Native-Architectures-Using-Microservices-1-2026-10-21T115447.071.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2240\" \/>\n\t<meta property=\"og:image:height\" content=\"1260\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Piyush Solanki\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Piyush Solanki\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Exploring the Android Room Database with Kotlin","description":"Learn the Android Room database with Kotlin: setup with KSP, DAOs with Flow, migrations, relations and testing, with code that compiles.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin\/","og_locale":"en_US","og_type":"article","og_title":"Exploring the Android Room Database with Kotlin","og_description":"Learn the Android Room database with Kotlin: setup with KSP, DAOs with Flow, migrations, relations and testing, with code that compiles.","og_url":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin\/","og_site_name":"200OK Solutions Blog","article_published_time":"2025-01-10T06:58:25+00:00","article_modified_time":"2026-10-21T06:43:46+00:00","og_image":[{"width":2240,"height":1260,"url":"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2025\/01\/How-to-Migrate-to-Cloud-Native-Architectures-Using-Microservices-1-2026-10-21T115447.071.png","type":"image\/png"}],"author":"Piyush Solanki","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Piyush Solanki","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":["Article","BlogPosting"],"@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin#article","isPartOf":{"@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin\/"},"author":{"name":"Piyush Solanki","@id":"https:\/\/www.200oksolutions.com\/blog\/#\/schema\/person\/e07f6b8e3c9a90ce7b3b09427d26155e"},"headline":"Exploring the Android Room Database with Kotlin","datePublished":"2025-01-10T06:58:25+00:00","dateModified":"2026-10-21T06:43:46+00:00","mainEntityOfPage":{"@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin\/"},"wordCount":1093,"publisher":{"@id":"https:\/\/www.200oksolutions.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin#primaryimage"},"thumbnailUrl":"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2025\/01\/How-to-Migrate-to-Cloud-Native-Architectures-Using-Microservices-1-2026-10-21T115447.071.png","keywords":["Android architecture components","Android development","Android persistence library","data persistence","Kotlin Room","Room Database","SQLite in Android"],"articleSection":["Android","Mobile"],"inLanguage":"en-US"},{"@type":["WebPage","SearchResultsPage"],"@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin\/","url":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin","name":"Exploring the Android Room Database with Kotlin","isPartOf":{"@id":"https:\/\/www.200oksolutions.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin#primaryimage"},"image":{"@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin#primaryimage"},"thumbnailUrl":"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2025\/01\/How-to-Migrate-to-Cloud-Native-Architectures-Using-Microservices-1-2026-10-21T115447.071.png","datePublished":"2025-01-10T06:58:25+00:00","dateModified":"2026-10-21T06:43:46+00:00","description":"Learn the Android Room database with Kotlin: setup with KSP, DAOs with Flow, migrations, relations and testing, with code that compiles.","breadcrumb":{"@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin#primaryimage","url":"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2025\/01\/How-to-Migrate-to-Cloud-Native-Architectures-Using-Microservices-1-2026-10-21T115447.071.png","contentUrl":"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2025\/01\/How-to-Migrate-to-Cloud-Native-Architectures-Using-Microservices-1-2026-10-21T115447.071.png","width":2240,"height":1260,"caption":"200OK Solutions blog banner titled \u201cExploring the Android Room Database with Kotlin,\u201d featuring a laptop, database interface, and digital communication icons illustrating Android app development and local data storage using Kotlin."},{"@type":"BreadcrumbList","@id":"https:\/\/www.200oksolutions.com\/blog\/exploring-android-room-database-with-kotlin#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.200oksolutions.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Blog > Android > Exploring the Android Room Database with Kotlin"}]},{"@type":"WebSite","@id":"https:\/\/www.200oksolutions.com\/blog\/#website","url":"https:\/\/www.200oksolutions.com\/blog\/","name":"Web Development, Software, and App Blog | 200OK Solutions","description":"","publisher":{"@id":"https:\/\/www.200oksolutions.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.200oksolutions.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.200oksolutions.com\/blog\/#organization","name":"200OK Solutions","url":"https:\/\/www.200oksolutions.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.200oksolutions.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2025\/09\/200ok_logo-CGzMrWDu.png","contentUrl":"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2025\/09\/200ok_logo-CGzMrWDu.png","width":500,"height":191,"caption":"200OK Solutions"},"image":{"@id":"https:\/\/www.200oksolutions.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.instagram.com\/200ok_solutions\/"]},{"@type":"Person","@id":"https:\/\/www.200oksolutions.com\/blog\/#\/schema\/person\/e07f6b8e3c9a90ce7b3b09427d26155e","name":"Piyush Solanki","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.200oksolutions.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2026\/05\/cropped-piyush-solanki-96x96.jpg","contentUrl":"https:\/\/www.200oksolutions.com\/blog\/wp-content\/uploads\/2026\/05\/cropped-piyush-solanki-96x96.jpg","caption":"Piyush Solanki"},"description":"Piyush is a seasoned PHP Tech Lead with 10+ years of experience architecting and delivering scalable web and mobile backend solutions for global brands and fast-growing SMEs. He specializes in PHP, MySQL, CodeIgniter, WordPress, and custom API development, helping businesses modernize legacy systems and launch secure, high-performance digital products. He collaborates closely with mobile teams building Android &amp; iOS apps , developing RESTful APIs, cloud integrations, and secure payment systems using platforms like Stripe, AWS S3, and OTP\/SMS gateways. His work extends across CMS customization, microservices-ready backend architectures, and smooth product deployments across Linux and cloud-based environments. Piyush also has a strong understanding of modern front-end technologies such as React and TypeScript, enabling him to contribute to full-stack development workflows and advanced admin panels. With a successful delivery track record in the UK market and experience building digital products for sectors like finance, hospitality, retail, consulting, and food services, Piyush is passionate about helping SMEs scale technology teams, improve operational efficiency, and accelerate innovation through backend excellence and digital tools.","url":"https:\/\/www.200oksolutions.com\/blog\/author\/piyush\/"}]}},"_links":{"self":[{"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/posts\/1719","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/comments?post=1719"}],"version-history":[{"count":6,"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/posts\/1719\/revisions"}],"predecessor-version":[{"id":5287,"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/posts\/1719\/revisions\/5287"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/media\/5284"}],"wp:attachment":[{"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=1719"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=1719"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.200oksolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=1719"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}