ROOM Database

ROOM Database

温馨提示:本文最后更新于2025-12-18 14:17:47,某些文章具有时效性,若有错误或已失效,请在下方留言

Room DatabaseAndroid 官方提供的本地数据库框架,是 Jetpack 架构组件的一部分,用于在应用中以安全、简洁、高效的方式存储本地数据。它在 SQLite 之上提供了更高层的抽象,让开发者更容易读写数据库,并减少样板代码。

核心概念

Room Database 有三个主要的核心概念

  • Entity
  • DAO(Database Access Object)
  • Database

Entity 实体

  • 对应数据库中的一张表
  • 每个字段对应表中的一列
  • 使用 @Entity 注解定义
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity
public class User {
    @PrimaryKey(autoGenerate = true)
    public int uid;

    public String name;
    public int age;
}

DAO 数据库访问对象

  • 定义访问数据库的方法(增删改查)
  • Room 会根据 DAO 自动生成实际 SQL 代码
  • 使用 @Dao 注解
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;

import java.util.List;

@Dao
public interface UserDao {
    @Query("SELECT * FROM User")
    List<User> getAll();

    @Insert
    void insert(User user);

    @Delete
    void delete(User user);
}

Database 数据库定义

  • 继承 RoomDatabase
  • 使用 @Database 注解
  • 将所有 EntityDAO 注册进去
import androidx.room.Database;
import androidx.room.RoomDatabase;

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

基本使用

1. 添加依赖

bundle.gradle.kts 文件中,添加 ROOM 相关的依赖。

// build.gradle.kts 文件
plugins {
    ...
    alias(libs.plugins.ksp)
}
dependencies {
    ...
    // Room DB
    implementation(libs.androidx.room.runtime)
    ksp(libs.androidx.room.compiler)
    implementation(libs.androidx.room.ktx)
}
# libs.version.toml 文件
[versions]
...
kotlin = "2.2.0"
roomRuntime = "2.8.4"

[libraries]
...
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }

[plugins]
...
# ksp 版本要与 kotlin 版本一致
ksp = { id = "com.google.devtools.ksp", version = "2.2.0-2.0.2" } 
// build.gradle.kts 文件
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    ...
    alias(libs.plugins.ksp) apply false
}

添加完成后,点击 Sync Now 按钮,开始同步配置。

2. 创建 Entity 实体

Entity 是表示 SQLite 数据库中的表的基本组件。新建 Contacts 实体,实体包含 idname 以及 email

package com.stewednoodles.contactsmanagerapp;

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

/**
 * 实体类
 * @Entity: 将 tableName 与实体类 Movie 进行映射
 */
@Entity(tableName = "movies_table")
data class Movie(
    @PrimaryKey
    val id: Int,
    val title: String,
    val overview: String,

    @ColumnInfo(name = "poster_path")
    val movieImg: String
)

3. 创建 DAO

DAO 是一个接口,定义了一系列用于对 Entity 数据库表执行数据库操作的方法,例如 insetupdatedeletequery 以及其他操作。

DAO 是一个用 @Dao 注解的接口。

@Dao
interface MovieDAO {
    @Insert
    suspend fun insert(movie: Movie)

    @Insert
    suspend fun insetMovieList(movies: List<Movie>)

    @Query("SELECT * FROM movies_table")
    suspend fun getAllMoviesInDB(): List<Movie>
}

4. 创建 Database 类

Database 类是一个抽象类,用作数据库的持有者。

/**
 * 定义了数据库中的实体(表)和版本号
 *  - 数据库变更时,可以更新版本号
 */
@Database(entities = [Movie::class], version = 1)
abstract class MovieDB : RoomDatabase() {
    // DAO
    abstract val movieDAO: MovieDAO

    companion object {
        // Volatile在多线程中避免条件竞争
        @Volatile
        private var INSTANCE: MovieDB? = null

        fun getInstance(context: Context): MovieDB {
            synchronized(this) {
                var instance = INSTANCE
                if (instance == null) {
                    instance = Room.databaseBuilder(
                        context = context.applicationContext,
                        klass = MovieDB::class.java,
                        name = "movies_db"
                    ).build()
                }
                INSTANCE = instance

                return instance
            }
        }
    }
}

5. 创建 Repository

Repository 可以从不同的数据源(不同的 REST API、缓存、本地数据库存储)收集数据,并将这些数据提供给应用程序的其余部分。有效递将数据源与应用程序的其他部分隔离开来,并为应用程序的其余部分提供一个干净的数据访问 API。

DAO 中的方法从 Repository 执行。

class Repository(context: Context) {
    // 从网络获取数据
    suspend fun getPopularMoviesFromOnline(apiKey: String): List<Movie> {
        return RetrofitInstance.api.getPopularMovies(apiKey).results
    }

    // 从数据库中获取数据
    private val db = MovieDB.getInstance(context)
    private val movieDAO: MovieDAO = db.movieDAO

    suspend fun getMoviesFromDB(): List<Movie> {
        return movieDAO.getAllMoviesInDB()
    }

    suspend fun insertMoviesIntoDB(movies: List<Movie>) {
        movieDAO.insetMovieList(movies)
    }

    suspend fun insertMovieIntoDB(movie: Movie) {
        movieDAO.insert(movie)
    }
}

6. ViewModel

class MovieViewModel(repository: Repository) : ViewModel() {

    // ViewModel 使用 mutableStateOf<> 而不是 LiveData
    // 从而直接由 Compose 进行状态管理
    //
    // 当 'movies' 的值发生变化时,Compose 会自动
    // 重新组合(recompose)所有依赖该状态的 UI 部分
    var movies by mutableStateOf<List<Movie>>(emptyList())
        private set

    // 从网络中获取
    var moviesFromApi by mutableStateOf<List<Movie>>(emptyList())
        private set

    // 从本地数据库中获取
    var moviesFromRoomDB by mutableStateOf<List<Movie>>(emptyList())
        private set

    init {
        viewModelScope.launch {
            try {
                // 从网络抓取数据
                moviesFromApi = repository.getPopularMoviesFromOnline("172dc15a2a4598f149f4d87f13bbbc59")

                // 数据存入数据库
                repository.insertMoviesIntoDB(moviesFromApi)

                // 赋值
                movies = moviesFromApi
            } catch (e: Exception) {
                // 从数据库获取影视数据
                moviesFromRoomDB = repository.getMoviesFromDB()
                movies = moviesFromRoomDB
            }
        }
    }
}
© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容