Android 数据存储(四)-Room

2023-05-16

目录

一、概述

1.1 描述

1.2 主要部件

二、创建 Room

2.1 添加依赖项

2.2 创建数据实体

2.2.1 设置 tableName or name 属性

2.2.2 设置主键

2.2.3 忽略字段

2.3 创建数据访问对象 (DAO)

2.4 创建数据库

三、使用 Room

3.1 创建数据库

3.2 添加数据

3.3 查找数据

3.4 修改数据

3.5 删除数据

四、相关链接


一、概述

1.1 描述

        上文介绍到了SQLite Api,今天咱们介绍一下 Room ( Android Jetpack 重要成员之一 )。

        Room 持久性库在 SQLite 之上提供了一个抽象层,以允许流畅的数据库访问,同时利用 SQLite 的全部功能。特别是,Room 提供以下好处:

  • SQL 查询的编译时验证。

  • 方便的注释,最大限度地减少重复和容易出错的样板代码。

  • 简化的数据库迁移路径。

        由于这些考虑,我们强烈建议您使用 Room 而不是直接使用 SQLite API。

1.2 主要部件

Room 包含三个主要组件:

  • 保存数据库并用作与应用程序持久数据的底层连接的主要访问点的数据库类

  • 表示应用数据库中表的数据实体

  • 数据访问对象(DAO),提供你的应用可用于在数据库中查询、更新、插入和删除数据的方法。

        数据库类为您的应用程序提供与该数据库关联的 DAO 实例。反过来,应用程序可以使用 DAO 从数据库中检索数据作为关联数据实体对象的实例。该应用程序还可以使用定义的数据实体来更新相应表中的行,或创建新行以进行插入。下图说明了 Room 不同组件之间的关系。

二、创建 Room

2.1 添加依赖项

        在 app/build.gradle文件中添加依赖:

dependencies {
    //Room
    def room_version = "2.4.1"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
}

2.2 创建数据实体

        将每个 Room 实体定义为一个使用 @Entity 注释的类。 Room 实体包括数据库中对应表中每一列的字段,包括一个或多个构成主键的列。因此,实体类往往是不包含任何逻辑小型模型类。我们的 User 类代表数据库中数据的模型,告诉 Room 它应该基于这个类创建一个表:

@Entity
public class User {
    @PrimaryKey
    public int id;
    
    public String mName;
    public int mAge;
}

        通过将 @PrimaryKey 注释添加到正确的字段来设置主键(如:id)。

注意:要保留一个字段,Room 必须有权访问它。 你其设为 public 或为其提供 getter 和 setter 方法来确保 Room 可以访问该字段。

默认情况下:

  • 数据库表名:Room 使用类名作为数据库表名。或通过 @Entity 注释的 tableName 属性来设置表的名称。

  • 列名:Room 默认使用字段名作为数据库中的列名。或通过 @ColumnInfo 注释添加到字段并设置 name 属性来修改列名。

2.2.1 设置 tableName or name 属性

@Entity(tableName = "users")
public class User {
    @PrimaryKey
    public int id;

    @ColumnInfo(name = "name")
    public String mName;
    @ColumnInfo(name = "age")
    public int mAge;
}

        此时我们将表名设置为 users 。将 mName 和 mAge 在表中的列名分别修改为 name 和 age。

注意:SQLite 中的表名和列名不区分大小写。

2.2.2 设置主键

1、定义单主键

        每个 Room 实体必须定义一个主键,用于唯一标识相应数据库表中的每一行。最直接的方法是使用 @PrimaryKey 注释单个列,如上面 User 类中的 id 属性

注意:如果你需要 Room 为实体实例分配自动 ID,请将 @PrimaryKey 的 autoGenerate 属性设置为 true

@Entity(tableName = "users")
public class User {
    @PrimaryKey(autoGenerate = true)
    public int id;
}

2、定义复合主键

        如果你需要通过多个列的组合来唯一标识实体的实例,你可以通过在 @Entity 的 primaryKeys 属性中列出这些列来定义复合主键:

@Entity(tableName = "users",primaryKeys = {"mName","mAge"})
public class User {
    public String mName;
    public int mAge;
}

2.2.3 忽略字段

        默认情况下,Room 为实体中定义的每个字段创建一个列。但是某些字段我们不需要保存到数据库,可以使用@Ignore 对其进行注释。

@Entity(tableName = "users")
public class User {
    @Ignore      //@Ignore 此属性不在数据库生产列
    public String nickname;
}

2.3 创建数据访问对象 (DAO)

        DAO 负责定义访问数据库的方法。使用 Room,我们不需要所有与 Cursor 相关的代码,只需使用 UserDao 类中的注释定义我们的查询即可。每个 DAO 都包含提供对应用程序数据库的抽象访问的方法在编译时,Room 会自动生成您定义的 DAO 的实现

        你可以将 DAO 定义为接口或抽象类。对于基本用例,通常应该使用接口。无论哪种情况,都必须使用 @Dao 注释你的 DAO。DAO 没有属性,但它们确实定义了一种或多种方法来与应用程序数据库中的数据进行交互。

@Dao //这个是必须的
public interface UserDao {
    //无需编写任何 SQL 代码即可在数据库中插入、更新和删除行的便捷方法。
    //新增单个实体
    @Insert
    void insertUser(User user);
    //新增多个实体
    @Insert
    void insertUsers(List<User> users);

    //更新数据
    @Update
    void updateUser(User user);
    //删除数据
    @Delete
    void deleteUser(User user);
    
    //编写自己的 SQL 查询(query)方法
    //查询 users 表
    @Query("SELECT * FROM users")
    List<User> getAll();

    //根据name查询 users 表,将参数集合传递给查询
    @Query("SELECT * FROM users WHERE name IN (:usernames)")
    List<User> loadAllByNames(int[] usernames);

    //将简单参数传递给查询
    @Query("SELECT * FROM users WHERE age > :minAge")
    public User[] loadAllUsersOlderThan(int minAge);
}

2.4 创建数据库

        定义一个 AppDatabase 类来保存数据库。AppDatabase 定义数据库配置并充当应用程序对持久数据的主要访问点。数据库类必须满足以下条件:

  • 该类必须使用 @Database 注释进行注释,该注释包括一个实体数组,该数组列出了与数据库关联的所有数据实体。

  • 该类必须是扩展 RoomDatabase 的抽象类。

  • 对于与数据库关联的每个 DAO 类,数据库类必须定义一个具有零参数并返回 DAO 类实例的抽象方法。

import androidx.room.Database;

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

        Room 的三个主要组件已经创建好了,咱们开始使用吧。

三、使用 Room

3.1 创建数据库

        数据库保存路径:/data/data/com.scc.datastorage/files/room_db

注意:数据库的操作一定要放到子线程中,切不可在主线程中操作(否则会报错),虽然可以强制开启允许这么做,但不要在主线程中操作数据库,避免遇到ANR问题。

            //创建数据库
        String dir = getFilesDir() + "/room_db";
        AppDatabase db = Room.databaseBuilder(AppGlobalUtils.getApplication(),
                AppDatabase.class, dir)
                .build();

允许在主线程中进行数据库操作:

        在构建AppDatabase实例的时候,添加allowMainThreadQueries()方法,这样Room就可以在主线程中进行数据库操作了,但是这个方法只建议在测试环境下使用

        db = Room.databaseBuilder(AppGlobalUtils.getApplication(),
                AppDatabase.class, dir)
                .allowMainThreadQueries()
                .build();

3.2 添加数据

            List<User> list = new ArrayList<>();
            list.add(new User(10, "帅次次", 20, "09:00"));
            list.add(new User(12, "朱元璋", 30, "11:00"));
            list.add(new User(15, "赵匡胤", 40, "13:00"));
            list.add(new User(18, "李世民", 50, "15:00"));
            new Thread(() -> {
                userDao.insertUsers(list);
                Log.e("Room", "插入成功:" + db.userDao().queryAll().size());
            }).start();

3.3 查找数据

            new Thread(() -> {
                StringBuilder sql = new StringBuilder();

                List<User> list = userDao.queryAll();
                sql.append(list.size());
                if (list.size() > 0) {
                    for (User bean : list) {
                        sql.append("\n").append(bean.toString());
                    }
                }
                Log.e("Room", sql.toString());
            }).start();

3.4 修改数据

            new Thread(() -> {
                StringBuilder sql = new StringBuilder();
                List<Integer> id = new ArrayList<>();
                id.add(18);
                List<User> list = db.userDao().queryAllById(id);
                if (list.size() > 0) {
                    sql.append("\n 修改数据前:").append(list.get(0).toString());
                }
                User user = new User(18, "武媚娘", 32, "12:00");
                userDao.updateUser(user);
                list = db.userDao().queryAllById(id);
                if (list.size() > 0) {
                    sql.append("\n 修改数据后:").append(list.get(0).toString());
                }
                Log.e("Room", String.valueOf(sql));
            }).start();

3.5 删除数据

                new Thread(() -> {
                    StringBuilder sql = new StringBuilder();
                    List<User> list = userDao.queryAll();
                    if (list.size() > 0) {
                        for (User bean : list) {
                            sql.append("\n").append(bean.toString());
                        }
                    }
                    User user = new User();
                    user.id = 12;
                    db.userDao().deleteUser(user);
                    sql.append("\n删除后");
                    List<User> listDelete = db.userDao().queryAll();
                    Log.e(getClass().getName(), "list:" + list.size());
                    if (listDelete.size() > 0) {
                        for (User bean : listDelete) {
                            sql.append("\n").append(bean.toString());
                        }
                    }
                    Log.e("Room", sql.toString());
                }).start());

        然后你会发现 Room 和 SQLite的用法基本一致。操作简单易上手,说试试就试试。

四、相关链接

Android 数据全方案处理

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 数据存储(四)-Room 的相关文章

  • 使用DBFlow,如何加密已经存在的数据库?

    我正在使用 DBFlow 来处理项目中的数据库 并且我想对现有数据库进行加密 我知道我可能必须删除现有的未加密数据库并创建另一个加密数据库 我也知道我可以将 SQLCipher 与 DBFlow 一起使用 如上所述文档 https gith
  • Xamarin 分步向导 Android 视图

    我想在 Xamarin c 中构建一个 android 活动 用于逐步注册和 或信息 我怎样才能做这样的事情 谁能给我一个代码示例或其他东西 谢谢 基本上你需要使用一个名为 a 的元素ViewPager 并且每个页面都会不同Fragment
  • 升级到最新支持库后Android JACK编译器错误

    Android Studio 2 2 3 Windows 10 64位 构建工具版本 25 Android Gradle插件版本2 2 3 升级到最新的支持库 从 23 4 0 到 25 1 0 并更改编译版本 从 23 到 25 后 我收
  • Android ToggleButton 始终检查

    如果切换按钮处于选中或取消选中状态 我想存储在 SharedPreferences 中 toggle setOnCheckedChangeListener new OnCheckedChangeListener public void on
  • 音频流的最佳实践

    我正在编写一个应用程序来播放远程服务器的音频 我尝试了多种方法来实现流音频 但它们对我来说都不够好 这就是我尝试过的 幼稚地使用 MediaPlayer 就像是 MediaPlayer player new MediaPlayer play
  • 无法在 Android Studio 中导出签名的 APK

    当我使用keytool list keystore path to keyfile jks并提供我的密码 我可以看到那里的条目 但是当我在尝试使用相同的密码生成签名的 APK 时使用相同的密码时 我收到错误 无法加载密钥库 密钥库被篡改 或
  • 4 天后,应用仍未在 Google Play 搜索中编入索引

    我已经在 4 天前在 google play 上发布了我的第一个应用程序 语音到短信 但我仍然无法通过其名称或开发人员名称在搜索中找到我的应用程序 我只能通过包名称或真实应用程序名称 但不带空格 找到我的应用程序 VoiceToSMS 链接
  • CoordinatorLayout 和 ImageView 在滚动时调整宽度的问题

    我正在尝试放置一个ImageView in a CollapsingToolbarLayout它在加载时占据整个屏幕 并且当您滚动内容时 16x9 分辨率图像宽度会调整大小 直到图像占据屏幕的整个宽度 那时 我希望图像具有视差app lay
  • 在 NFC 标签扫描期间,onNewIntent() 内的intent.getAction() 为 null

    这是我第一次使用 NFC 标签 我在清单中声明了 NFC 扫描活动
  • Toast 消息消失后​​完成活动吗?

    有谁知道 是否有可能对 Toast 消息执行某些操作 在我的情况下完成活动 将被关闭 您只需创建一个Thread持续时间只要Toast显示 然后您就可以完成您的Activity public void onCreate Bundle sav
  • FragmentActivity 无法解析为类型

    我正在尝试来自的应用程序这个博客 http android developers blogspot com 2011 02 android 30 fragments api html 在延长的同时FragmentActivity 我收到以下
  • 模拟器无法加载

    我正在使用 hello android 教程并通过 eclipse 创建 avd 启动模拟器时不使用图像 它只是显示一个黑色的后屏 中间有 ANDROID 字样 并且在 ANDROID 字样的末尾有一个闪烁的光标 我已按照 T 的步骤安装
  • Android:WebView/BaseInputConnection 中的退格键

    我在 Android 4 2 中遇到软键盘退格问题 我在 WebView CodeMirror 中有一个自定义编辑器 它使用一个空的
  • 我在 android studio 中使用 kotlin 时出现错误

    为什么会出现这个错误 09 12 16 36 31 502 1886 1886 com getloction nourmedhat smartgate getlocation E AndroidRuntime 致命异常 main 进程 co
  • Android Google Map V2:如何在单击另一个标记时更改先前单击的标记的图标

    更新 我通过添加 previousMarker 对象解决了性能问题 因此 只有先前单击的标记将被删除并替换为默认图标 但是 当我单击标记时 信息窗口仍然不显示 我有一个地图视图并在上面设置了一些标记 我想要的是 当我单击一个标记时 它将其图
  • 使用 Proguard 通过 Dropbox.com 库混淆 Android 应用程序

    我刚刚创建了一个需要 Dropbox com API 库的 Android 应用程序 我现在尝试在 发布 模式下构建应用程序 并希望在代码上运行混淆器以对其进行混淆 但是 每当我尝试运行 Proguard 时 都会收到以下错误 Progua
  • Android 的 Intent 和 Parcelable 对象

    为什么我需要打包我的对象 即使我只需将其发送到同一任务的另一个线程 实际上 我需要打开一个甚至可以在同一线程 主线程 上运行的活动 换句话说 为什么 Google 不提供一个 startActivity 版本 它采用通用对象广告参数而不是捆
  • Android:透明活动问题

    最近 在我们的一款生产应用程序上 透明活动已停止工作 我的意思是它变成了黑色背景而不是透明背景 当我将活动的背景颜色设置为纯色 即红色 绿色等 时 它的应用不会出现问题 该问题可能是由于迁移到 AndroidX 引起的 但我没有这方面的证据
  • 在数组列表中过滤 Filterable 不取消之前的过滤

    我看过过滤器方法文档 其中显示调用过滤器会取消所有先前未执行的过滤请求 并发布一个稍后将执行的新过滤请求 但我收到的实际回调有些不同 在我的实现中 它不会取消先前的过滤器请求并调用publishResults 最近一次搜索条件后的上一次搜索
  • 如何从DataSource.Factory获取数据

    我必须调用此方法才能获取所有人员 我根本无法修改这个方法 Query SELECT FROM PERSON TABLE ORDER BY NAME DESC abstract fun getElements DataSource Facto

随机推荐

  • ubuntu20.04安装中文输入法

    虽然搜狗的官网已经宣传说已经支持2004 2010 xff0c 但是支持的并不完美 xff0c 闪退 xff0c 打不出字各种问题不断 xff0c 所以本文带领大家安装几款能够正常使用的中文输入法 但是正在我要发这篇博客的时候 xff0c
  • R语言做柱状图

    R语言做柱状图 转自 xff1a http www phperz com article 16 0102 180120 html 条形图代表在与条成比例的变量的值的长度矩形条数据 R使用函数barplot 来创建柱状图 R能够绘制柱状图垂直
  • R语言 PCA(主成分分析)

    R语言 PCA 转自 xff1a http www cnblogs com longzhongren p 4300593 html 1 关键点 综述 xff1a 主成分分析 因子分析 典型相关分析 xff0c 三种方法的共同点主要是用来对数
  • 使用Pandas对数据进行筛选和排序

    使用Pandas对数据进行筛选和排序 转自 xff1a http bluewhale cc 2016 08 06 use pandas filter and sort html 筛选和排序是Excel中使用频率最多的功能 xff0c 通过这
  • linux 下安装blat软件

    linux 下安装blat软件 blat是一款很经典的比对工具 xff0c 与blast相比 xff0c 具有速度快 共线性输出比对结果等优点 但是 xff0c blat源码包里面的README文件写得很不清楚 xff0c 这里 xff0c
  • 基于统计的压缩算法:游程编码

    原网址 xff1a http www cnblogs com xudong bupt p 3761417 html 基于统计的压缩算法 xff1a 游程编码 1 游程编码概念 游程编码又称 运行长度编码 或 行程编码 xff0c 是一种统计
  • BWT (Burrows–Wheeler_transform)数据转换算法

    原网址 xff1a https blog csdn net luanzheng 365 article details 78575429 BWT Burrows Wheeler transform 数据转换算法 1 什么是BWT 压缩技术主
  • pip使用豆瓣的镜像源

    抄自 xff1a https www cnblogs com ZhangRuoXu p 6370107 html pip使用豆瓣的镜像源 豆瓣镜像地址 xff1a https pypi douban com simple 虽然用easy i
  • PyVCF

    抄自 xff1a https www cnblogs com nkwy2012 p 9204088 html vcf文件的全称是variant call file xff0c 即突变识别文件 xff0c 它是基因组工作流程中产生的一种文件
  • 【Kotlin 初学者】扩展-享受编程

    作者简介 xff1a CSDN博客专家 华为云 云享专家认证 系列专栏 xff1a Kotlin 初学者 学习交流 xff1a 三人行必有我师焉 xff1b 择其善者而从之 xff0c 其不善者而改之 目录 一 介绍 二 扩展函数 2 1
  • 【Kotlin 初学者】函数式编程

    作者简介 xff1a CSDN博客专家 华为云 云享专家认证 系列专栏 xff1a Kotlin 初学者 五星好评 xff1a 左侧点一下 网页端 xff0c 移动端 xff1a https bbs csdn net topics 6039
  • centos8.5 更新失败

    今天使用yum makecache的时候出现了Error Failed to download metadata for repo 39 base 39 Cannot download repomd xml Cannot download
  • 【Kotlin 初学者】Java和Kotlin互操作

    作者简介 xff1a CSDN博客专家 华为云 云享专家认证 系列专栏 xff1a Kotlin 初学者 五星好评 xff1a 左侧点一下 网页端 xff0c 移动端 xff1a https bbs csdn net topics 6039
  • Kotlin 基础知识汇总(知识与实践相结合)

    2个月的时间总算把 Kotlin 的基础知识写完了 xff0c 下面咱们看看具体内容 xff1a 学习 Kotlin 的必要性 Kotlin 初学者 为什么要学Kotlin Kotlin 初学者 打牢基础的重要性 运行环境 Kotlin 初
  • HashMap的产生与原理

    一 HashMap的诞生 1 1 数组 数组 xff1a 一片物理上连续的大小确定的储存空间 好处 xff1a 根据下标快速的查找和修改里面的内容 缺点 xff1a 大小确定 xff0c 无法修改 添加新的元素或者删除元素比较麻烦 数组的静
  • Android 数据存储(一)-文件存储

    目录 一 数据存储概念 二 应用程序专属文件存储 2 1 访问持久文件 2 2 将数据存储到文件 2 3 从文件中读取数据 2 4 查看文件列表 2 5 删除文件 三 缓存文件 cache目录下 3 1 创建缓存文件 3 2 删除文件 四
  • 回顾2021,展望2022 | 年终总结

    你付出多少努力 xff0c 就必有多少收获 一 回顾 2021 2021 年输出109篇文章 xff0c 收获 xff1a 博客专家认证 Android领域新星创作者认证 博客之星Top50 同时也在问答模块解决了部分小伙伴的问题 xff0
  • Android 数据存储(二)-SP VS DataStore VS MMKV

    一 SharedPreferences 不同于文件的存储方式 xff0c 如果要保存的键值集合相对较小 xff0c 则应使用SharedReferences API SharedReferences对象指向一个包含键值对的文件 xff0c
  • Jetpack DataStore 你总要了解一下吧?

    目录 一 DataStore 介绍 Preferences DataStore 和 Proto DataStore 二 Preferences DataStore 2 1 添加依赖 2 2 使用 Preferences DataStore
  • Android 数据存储(四)-Room

    目录 一 概述 1 1 描述 1 2 主要部件 二 创建 Room 2 1 添加依赖项 2 2 创建数据实体 2 2 1 设置 tableName or name 属性 2 2 2 设置主键 2 2 3 忽略字段 2 3 创建数据访问对象