RoomでSQLiteデータベースを操作

KotlinでSQLiteデータベースに接続する書き方を解説します。今回はJetpackCompose&RoomでSQLiteデータベースを操作する方法になります。 データベースの操作を行う方法は他にもあります。以前の記事はコチラをご覧ください。【SQLiteデータベースに接続】
今回の開発環境は「Android Studio Giraffe | 2023.3.1 Patch 2」を使用しており、UI製作は「Jetpack Compose」です。 あれこれ調べながら作成しましたが、未熟なため、いろいろおかしい箇所もあるかと思います。もし流用される場合は、適宜修正してください。ご利用は自己責任でお願いいたします。

※この記事は2023/11/11時点の情報です。

build.gradle
【公式ページ】を参考に Roomに必要な依存関係を追加します。今回は現時点で最新の2.5.0にしましたが、必要に応じて変更してください。

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    //以下を追加
    id 'kotlin-kapt'
}

android {
    namespace 'com.example.roomtest'
    compileSdk 34

    defaultConfig {
        applicationId "com.example.roomtest"
        minSdk 30
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary true
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = '17'
    }
    buildFeatures {
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion '1.4.3'
    }
    packaging {
        resources {
            excludes += '/META-INF/{AL2.0,LGPL2.1}'
        }
    }
    kapt {
        useBuildCache = false
        correctErrorTypes = true
        mapDiagnosticLocations = true
        arguments {
            arg("room.schemaLocation", "$projectDir/schemas".toString())
        }
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
    implementation 'androidx.activity:activity-compose:1.8.0'
    implementation platform('androidx.compose:compose-bom:2023.03.00')
    implementation 'androidx.compose.ui:ui'
    implementation 'androidx.compose.ui:ui-graphics'
    implementation 'androidx.compose.ui:ui-tooling-preview'
    implementation 'androidx.compose.material3:material3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    androidTestImplementation platform('androidx.compose:compose-bom:2023.03.00')
    androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
    debugImplementation 'androidx.compose.ui:ui-tooling'
    debugImplementation 'androidx.compose.ui:ui-test-manifest'

    //公式ページを参考にRoomに必要な依存関係を追加 (以下を全部)
    implementation("androidx.room:room-runtime:2.5.0")
    annotationProcessor("androidx.room:room-compiler:2.5.0")

    // To use Kotlin annotation processing tool (kapt)
    kapt("androidx.room:room-compiler:2.5.0")

    // optional - Kotlin Extensions and Coroutines support for Room
    implementation("androidx.room:room-ktx:2.5.0")

    // optional - RxJava2 support for Room
    implementation("androidx.room:room-rxjava2:2.5.0")

    // optional - RxJava3 support for Room
    implementation("androidx.room:room-rxjava3:2.5.0")

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation("androidx.room:room-guava:2.5.0")

    // optional - Test helpers
    testImplementation("androidx.room:room-testing:2.5.0")

    // optional - Paging 3 Integration
    implementation("androidx.room:room-paging:2.5.0")
}

User.kt
@Entityアノテーションは、このクラスがデータベースのエンティティであることを示し、 対応するデータベースのテーブルの名前を指定します。この例では、テーブルの名前は「users」です。
@PrimaryKeyアノテーションにはテーブルのプライマリキーを指定します。 autoGenerate = trueを記述することで、このプライマリキーの値が自動的に生成されます。
@ColumnInfoアノテーションは、データベース内の各カラム名と関連付けするために使用されるアノテーションです。 データベース内の列名をわかりやすく指定することでKotlinの変数名やフィールド名を変更しても データベーススキーマが変更されず、互換性が保たれます。 また、このアノテーションは省略可能であり、省略した場合は変数やフィールドの名前がそのまま列名として使用されます。

package com.example.roomtest

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    @ColumnInfo(name = "name") val name: String,
    @ColumnInfo(name = "age") var age: Int
)

UserDao.kt
データベースにアクセスし、データを操作するためのメソッドを提供するDAO(データアクセスオブジェクト)です。
@Insertアノテーションはエンティティをデータベースに挿入するためのメソッドです。
@Updateアノテーションはエンティティをデータベースに更新するためのメソッドです。
@Deleteアノテーションはエンティティをデータベースから削除するためのメソッドです。
@QueryアノテーションはカスタムのSQLを実行するためのメソッドです。

package com.example.roomtest

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update

@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAllUsers(): List

    @Query("SELECT * FROM users WHERE id = :userId")
    fun getUserById(userId: Long): User?

    @Insert
    fun insertUser(user: User)

    @Update
    fun updateUser(user: User)

    @Delete
    fun deleteUser(user: User)

    @Query("DELETE FROM users WHERE id = :userId")
    fun deleteUserById(userId: Long)
}

AppDatabase.kt
SQLiteデータベースへのアクセスを抽象化して簡単に扱えるようにします。 このクラスを使用すると、Roomはデータベースの作成とバージョンの管理を行い、 データベースへのアクセスが提供されます。
@Databaseアノテーションは、データベースクラスであることを示します。 パラメータにはデータベース内で使用するエンティティ(テーブル)のリストが含まれています。
RoomDatabase()はRoomDatabaseクラスを継承した抽象クラスです。このクラスはデータベースの実装を提供します。


package com.example.roomtest

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

MainActivity.kt
このアプリケーションのメインのソースです。usersテーブルへの追加・更新・削除を実行しています。
今回は新規プロジェクト作成で「Empty Activity」テンプレートを選択した際に自動作成された「MainActivity.kt」を修正しました。 新規プロジェクト作成に関してはコチラの記事を参考にしてください。【はじめてのAndroidアプリケーション(Kotlin)】


package com.example.roomtest

import android.annotation.SuppressLint
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.room.Room
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    @SuppressLint("CoroutineCreationDuringComposition")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            // データベースのインスタンスを取得
            val db = Room.databaseBuilder(
                applicationContext,
                AppDatabase::class.java, "user-database"
            ).build()

            // トランザクション内でデータベース操作を実行
            GlobalScope.launch(Dispatchers.IO) {
                db.runInTransaction {
                    try {
                        // UserDaoのインスタンスを取得
                        val userDao = db.userDao()

                        // 全ユーザーの取得と削除
                        var users = userDao.getAllUsers()
                        for (user in users) {
                            userDao.deleteUser(user)
                        }

                        // 登録前の件数表示
                        users = userDao.getAllUsers()
                        println("登録前 件数: ${users.size}")

                        // ユーザーの追加
                        val newUser = User(name = "チワワちゃん", age = 1)
                        userDao.insertUser(newUser)

                        // ユーザーの取得と表示
                        users = userDao.getAllUsers()
                        for (user in users) {
                            println("登録後 User: ${user.name}, Age: ${user.age}, ID: ${user.id}")
                        }

                        // ユーザーの更新
                        users[0].age = 2
                        userDao.updateUser(users[0])

                        val updatedUser = userDao.getUserById(users[0].id)
                        updatedUser?.let {
                            println("更新後 User: ${it.name}, Age: ${it.age}, ID: ${it.id}")
                        }

                        // ユーザーの削除
                        println("削除前 User: ${users[0].name}, Age: ${users[0].age}, ID: ${users[0].id}")
                        val userIdToDelete = updatedUser?.id ?: users[0].id
                        userDao.deleteUserById(userIdToDelete)

                        // 削除後の件数表示
                        users = userDao.getAllUsers()
                        println("削除後 件数: ${users.size}")

                        // エラーが発生しない場合は自動的にコミットされます

                    } catch (e: Exception) {
                        // 何らかのエラーが発生した場合はトランザクションが自動的にロールバックされます
                        throw e
                    }
                }
            }
        }
    }
}

@Composable
fun Greeting() {
    Text(
        text = "Roomに挑戦してみた件!"
    )
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    Greeting()
}

実行結果

登録前 件数: 0
登録後 User: チワワちゃん, Age: 1, ID: 18
更新後 User: チワワちゃん, Age: 2, ID: 18
削除前 User: チワワちゃん, Age: 2, ID: 18
削除後 件数: 0

ここまでJetpackComposeとRoomを使用してSQLiteデータベースを操作する方法を解説してきましたが理解できましたか?

SQLiteデータベースに接続する方法を覚えられましたか?

アプリケーション作成において、データベースの操作は避けて通れないと思います。 初心者の人は覚えることが多くて大変ですが、DBまわりのスキルを身につけておけば実践で役に立つので頑張って覚えましょう!

管理人情報