Android --- Room数据库(Java)

news2025/1/12 8:45:57

概念 

Room 是一个持久性库,属于 Android Jetpack 的一部分。Room 是 SQLite 数据库之上的一个抽象层。SQLite 使用一种专门的语言 (SQL) 来执行数据库操作。Room 并不直接使用 SQLite,而是负责简化数据库设置和配置以及与数据库交互方面的琐碎工作。此外,Room 还提供 SQLite 语句的编译时检查。

添加 Room 库

  1. 打开模块级 gradle 文件 build.gradle (Module: InventoryApp.app)。在 dependencies 块中,为 Room 库添加以下依赖项。
 // Room
 implementation ("androidx.room:room-runtime:2.2.6")
 implementation ("androidx.room:room-ktx:2.2.6")
 testImplementation ("androidx.room:room-testing:2.2.6")

Room 三大组件

  • 数据实体 Entry 表示应用的数据库中的表。数据实体用于更新表中的行所存储的数据以及创建新行供插入。
  • 数据访问对象 (DAO) 提供应用在数据库中检索、更新、插入和删除数据所用的方法。
  • 数据库类持有数据库,并且是应用数据库底层连接的主要访问点。数据库类为应用提供与该数据库关联的 DAO 的实例。

创建数据实体 Entry

实体类定义了一个表,该类的每个实例表示数据库表中的一行。实体类以映射告知 Room 它打算如何呈现数据库中的信息并与之交互。

@Entry 注解用于将某个类标记为数据库实体类。对于每个实体类,系统都会创建一个数据库表来保存相关项。除非另行说明,否则实体的每个字段在数据库中都表示为一列(如需了解详情,请参阅实体文档)。

存储在数据库中的每个实体实例都必须有一个主键。主键用于唯一标识数据库表中的每个记录/条目。主键一旦赋值就不能修改,只要它还存在于数据库中,它就表示相应的实体对象。

  • 在 数据类声明的上方,为该数据类添加 @Entity 注解。使用 tableName 参数为这个实体类指定 SQLite 表的名称。
  • 如需将 id 标识为主键,请为 id 属性添加 @PrimaryKey 注解。将参数 autoGenerate 设为 true,让 Room 为每个实体生成 ID。这样做可以保证每个商品的 ID 一定是唯一的。
  • 为其余属性添加 @ColumnInfo 注解。ColumnInfo 注解用于自定义与特定字段关联的列。例如,使用 name 参数时,您可以为字段指定不同的列名称,而不是变量名称。如下所示,使用参数自定义属性名称。此方法类似于使用 tableName 为数据库指定不同的名称。
// 账单数据类
@Entity(tableName = "AccountListItemTable")
public class AccountDataItem {
    @PrimaryKey(autoGenerate = true)
    private int id = 0;
    private String money; // 账单金额
    private String type; // 消费类别 -餐饮类
    private String detail; // 消费详情(备注)
    private String data; //消费时间
    private int in; // 1.收入 2.支出

    public AccountDataItem(String money, String type, String detail, String data, int in) {
        this.money = money;
        this.type = type;
        this.detail = detail;
        this.data = data;
        this.in = in;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getMoney() {
        return money;
    }

    public void setMoney(String money) {
        this.money = money;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public int getIn() {
        return in;
    }

    public void setIn(int in) {
        this.in = in;
    }

    @NonNull
    @Override
    public String toString() {
        return getId()+getDetail()+getMoney();
    }
}

创建数据访问对象 DAO

数据访问对象 (DAO) 是一种模式,其作用是通过提供抽象接口将持久性层与应用的其余部分分离。这种分离遵循您曾在之前的 Codelab 中接触过的单一责任原则。

DAO 的功能在于,让在底层持久性层执行数据库操作所涉及的所有复杂性都不波及应用的其余部分。这样就可以独立于使用数据的代码更改数据访问层。

public interface AccountDao{
    @Insert
    void insertAccount(AccountDataItem AccountDataItem);

    @Update
    void update(AccountDataItem AccountDataItem);
    
    @Query("SELECT * FROM AccountListItemTable")
    List<AccountDataItem> getAllData();
    
    @Query("DELETE FROM AccountListItemTable")
    void delete();
}

创建数据库实例

在此任务中,您将创建一个 RoomDatabase,它将使用您在上一个任务中创建的 Entity 和 DAO。该数据库类用于定义实体和数据访问对象的列表。它也是底层连接的主要访问点。

  • Room 是 SQLite 数据库之上的数据库层。
  • Room 可以帮您处理以前需要用 SQLiteOpenHelper 处理的日常任务 SQLiteOpenHelper。
  • Room 使用 DAO 向其数据库发出查询。
  • 默认情况下,为了避免糟糕的 UI 性能,Room 不允许您在主线程上发出查询。当 Room 查询返回 LiveData 时,查询会自动在后台线程上异步运行

在LiveData的官方文档中有提到LiveData可以和Room数据库一起使用

也就是说Room查询时可以直接返回一个LiveData对象,给这个LiveData对象添加观察者之后只要数据库数据发生改变都可以收到回调。

  • Room 提供 SQLite 语句的编译时检查。
package com.example.accountapp.data;

import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import com.example.accountapp.data.Dao.AccountListDao;
import com.example.accountapp.data.Entry.AccountDataItem;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Database(entities = {AccountDataItem.class},version = 1,exportSchema = false)
public abstract class AppRoomDataBase extends RoomDatabase {
    private static volatile AppRoomDataBase INSTANCE;
    public abstract AccountListDao accountListDao();

    static final ExecutorService databaseWriteExecutor = Executors.newFixedThreadPool(4);

    // 单例模式
    public static AppRoomDataBase getDataBase(Context context){
        if (INSTANCE == null) {
            synchronized (AppRoomDataBase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(
                                   context.getApplicationContext(),
                                    AppRoomDataBase.class,
                                    "记账数据库"
                            )
                            .build();
                }
            }
        }
        return INSTANCE;
    }
//    public abstract AccountDao accountDao();
}
  • Room 数据库类必须是 abstract 并扩展 RoomDatabase。通常,整个应用程序只需要一个 Room 数据库实例,数据库应该使用单例模式
  • 创建一个抽象 RoomDatabase 类,使用 @Database 注解将该类注释为 Room 数据库并在其中声明属于数据库的实体并设置版本号。
  • 每个实体对应于将在数据库中创建的表。数据库迁移超出了本代码实验室的范围,因此我们exportSchema在此处将其设置为 false 以避免出现构建警告。在实际应用中,您应该考虑为 Room 设置一个目录以用于导出架构,以便您可以将当前架构签入版本控制系统。
  • 通过每个@Dao的抽象“getter”方法来公开 DAO。
  • 创建了一个ExecutorService固定的线程池,您可以使用它在后台线程上异步运行数据库操作。 

数据存储库 

抽象了对多个数据源的访问。存储库不是架构组件库的一部分,但它是代码分离和架构的最佳实践。
为应用程序其余部分的数据访问提供了干净的 API。

使用

public class DataRepository {
    private AccountDao accountDao;
    private AccountListDao accountListDao;
    private Context context;
    AppRoomDataBase appRoomDataBase;

    public DataRepository(Context context) {
        this.context = context;
        appRoomDataBase = AppRoomDataBase.getDataBase(context);
        accountDao = appRoomDataBase.accountDao();
        accountListDao = appRoomDataBase.accountListDao();
    }

    public void insert(AccountDataItem accountDataItem) {
        AppRoomDataBase.databaseWriteExecutor.execute(new Runnable() {
            @Override
            public void run() {
                accountDao.insertAccount(accountDataItem);
            }
        });
    }

    public interface ListDataLoadListener {
        void onDataLoaded(List<AccountData> data);
    }

    public interface DataLoadListener {
        void onDataLoaded(List<AccountDataItem> data);
    }
    public void getData(DataLoadListener listener){
        AppRoomDataBase.databaseWriteExecutor.execute(() -> {
            List<AccountDataItem> accountDataItems = new ArrayList<>();
            accountDataItems.addAll(accountDao.getAllData());
            if(listener != null){
                listener.onDataLoaded(accountDataItems);
            }
        });
    }
}
  • DAO 被传递到存储库构造函数中,而不是整个数据库。这是因为您只需要访问 DAO,因为它包含数据库的所有读/写方法。无需将整个数据库公开给存储库。
  • 我们不需要在主线程上运行插入,所以我们使用ExecutorService在中创建的WordRoomDatabase在后台线程上执行插入

LiveData 

在数据存储库中的代码可见,每次数据库中的数据发生变化后,我们都需要开启一个工作线程去获取数据库中的内容,这不太方便,因此可以使用LiveData

LiveData,一个 用于数据观察的生命周期库类,解决了这个问题。在方法描述中使用LiveData类型的返回值 ,Room 会在数据库更新时生成所有必要的代码来更新LiveData。

注意:如果您LiveData独立于 Room 使用,则必须管理数据更新。LiveData没有公开可用的方法来更新存储的数据。

如果要更新存储在 LiveData中的数据,则必须使用 MutableLiveData而不是LiveData。该类MutableLiveData有两个公共方法允许您设置对象的值LiveData, setValue(T)和 postValue(T)。通常,MutableLiveData在 中使用 ViewModel,然后仅向观察者ViewModel公开不可变对象,LiveData

存储库 

public class DataRepository {
    private AccountDao accountDao;
    private AccountListDao accountListDao;
    private Context context;
    AppRoomDataBase appRoomDataBase;

    public DataRepository(Context context) {
        this.context = context;
        appRoomDataBase = AppRoomDataBase.getDataBase(context);
        accountDao = appRoomDataBase.accountDao();
        accountListDao = appRoomDataBase.accountListDao();
    }

    public void insert(AccountDataItem accountDataItem) {
        AppRoomDataBase.databaseWriteExecutor.execute(new Runnable() {
            @Override
            public void run() {
                accountDao.insertAccount(accountDataItem);
            }
        });
    }

    public LiveData<List<AccountDataItem>> getData() {
        return accountDao.getAllData();
    }
}

 Dao

@Dao
public interface AccountDao{
    @Insert
    void insertAccount(AccountDataItem AccountDataItem);

    @Update
    void update(AccountDataItem AccountDataItem);

    @Query("SELECT * FROM AccountListItemTable")
    LiveData<List<AccountDataItem>> getAllData();

    @Query("DELETE FROM AccountListItemTable")
    void delete();
}

使用:

 private void initData() {
        DataRepository dataRepository = new DataRepository(getContext());
        dataRepository.getData().observe(getViewLifecycleOwner(), new Observer<List<AccountDataItem>>() {
            @Override
            public void onChanged(List<AccountDataItem> accountDataItems) {
                System.out.println("数据更新了"+accountDataItems.size());
            }
        })

ViewModel

什么是 ViewModel?

ViewModel的作用是向 UI 提供数据并在配置更改后继续存在ViewModel充当 Repository 和 UI 之间的通信中心。可以使用ViewModel在 Fragment 之间共享数据。ViewModel 是 生命周期库的一部分。

ViewModel中,使用LiveData表示 UI 将使用或显示的可更改数据。使用LiveData有几个好处:

  • 您可以将观察者放在数据上(而不是轮询更改),并且仅在数据实际更改时更新 UI。
  • 存储库和 UI 完全由 ViewModel分开。
  • 没有来自的数据库调用ViewModel(这一切都在存储库中处理),从而使代码更易于测试。
public class AccountViewModel extends AndroidViewModel {
    private DataRepository dataRepository;
    private LiveData<List<AccountDataItem>> listLiveData;

    public AccountViewModel(@NonNull Application application) {
        super(application);
        dataRepository = new DataRepository(application);
        listLiveData = dataRepository.getData();
    }

    public LiveData<List<AccountDataItem>> getListLiveData() {
        return listLiveData;
    }
    public void insert(AccountDataItem accountDataItem){
        dataRepository.insert( accountDataItem);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1713605.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Python类的构造函数和析构函数

1、问题背景 当使用Python类时&#xff0c;可以使用构造函数和析构函数来初始化和清理类实例。构造函数在创建类实例时自动调用&#xff0c;而析构函数在删除类实例时自动调用。 在上面的代码示例中&#xff0c;Person类具有一个构造函数__init__和一个析构函数__del__。构造…

PHP MySQL图解学习指南:开启Web开发新篇章

PHP曾经是最流行的Web开发语言&#xff0c;许多世界领先的网站(如Facebook、维基百科和WordPress)都是用它编写的。PHP运行在Web服务器端&#xff0c;通过使用存储在MySQL数据库中的数据&#xff0c;使得网站可以为每一位访问者显示不同的定制页面。书中采用简单、直观的图示化…

大模型智力升级:AI的未来之路

大模型的发展引领了人工智能的新时代&#xff0c;其强大的数据处理和学习能力在医疗、金融、教育等众多领域取得了令人瞩目的成就。然而&#xff0c;随之而来的挑战也不容忽视。尽管大模型在特定任务上展现出了卓越的性能&#xff0c;但它们在理解复杂语境、处理未见情况的能力…

Linux内网中安装jdk1.8详细教程

本章教程,主要介绍如何在内网环境中配置JDK1.8环境变量 一、下载Linux版压缩包 下载地址:https://www.oracle.com/java/technologies/downloads/#java8 下载完成之后,通过XFTP等工具,将安装包上传到内网服务器 二、安装配置步骤 1、解压压缩包 tar -zxvf /usr/local/jdk-…

[Qt]关于QListWidget、QScrollArea 为什么在QDesigner上设置了之后界面上仍然不生效的问题

前言 最近做了一些有关QListWidget和QScrollArea的控件&#xff0c;我去&#xff0c;这两个控件是真的坑&#xff0c;明明我在QDesigner的操作界面上对这两个控件的界面进行了修改&#xff0c;但是编译出来的软件就是看上去什么都没有&#xff0c;很坑&#xff0c;Gpt也没解决…

html 引用vue3 element 首次加载缩成一团 挤在一起

问题&#xff1a;原生html引用vue,element plus 分析&#xff1a; vue.js, element脚本过大&#xff0c;首次加载网络慢的话&#xff0c;会是缩在这里&#xff0c;没完全渲染 解决&#xff1a; 没加载之前&#xff0c;不显示div&#xff0c;显示一个加载提示语 改动地方app…

CatDDoS僵尸网络与DNSBomb攻击:DDOS攻击最新变种

在近期的网络安全领域&#xff0c;两大严峻挑战浮出水面&#xff0c;为中国乃至全球的网络防护体系敲响了警钟。中国安全厂商奇安信的X实验室团队揭露了一波名为CatDDoS的恶意软件攻击浪潮&#xff0c;与此同时&#xff0c;一种创新且隐蔽的拒绝服务攻击技术——DNSBomb&#x…

揭秘IDM:数字资产管理的未来之星

在当今数字化时代&#xff0c;数字资产管理的重要性日益凸显。随着科技的飞速发展&#xff0c;越来越多的企业和个人开始关注如何有效管理和保护他们的数字资产。在这个过程中&#xff0c;IDM&#xff08;身份管理系统&#xff09;逐渐成为了热门话题。IDM作为一种新兴的技术手…

云原生网关 MSE-Higress

云原生网关 MSE-Higress 什么是云原生网关MSEMSE测评产品文档产品能力产品控制台 MSE与其他网关 什么是云原生网关MSE 在体验云原生网关 MSE-Higress功能之前&#xff0c;先了解一下什么是云原生网关 MSE&#xff0c;简单的说就是MSE就是遵循开源 Ingress/Gateway API 标准的下…

探数API分享-全球电价一览:谁最高,谁最低?

全球家庭用电价格的平均水平约为0.139美元/千瓦时&#xff0c;这是根据2021年12月的统计数据得出的。在这个平均水平之上&#xff0c;有一些国家的家庭用电价格远超过这个数值&#xff0c;特别是在欧洲的一些发达国家。 丹麦、荷兰、德国、英国、西班牙、比利时等国家的家庭用…

Token验证流程、代码示例、优缺点和安全策略,一文告诉你。

Token和Session都是用于身份验证和授权的机制&#xff0c;而且Token渐渐成为主流&#xff0c;有不少小伙伴对token的认识不全&#xff0c;这里给大家分享下。 一、什么是Token Token是一种用于身份验证和授权的令牌&#xff0c;通常用于在客户端和服务器之间进行安全的通信。…

小型水库水雨情和大坝安全监测解决方案

小型水库水雨情和大坝安全监测解决方案 小型水库作为重要的水资源管理和防洪调蓄设施&#xff0c;在保障农业灌溉、居民饮水及防洪安全方面发挥着不可或缺的作用。然而&#xff0c;由于其规模限制&#xff0c;小型水库往往在水雨情监测和大坝安全评估方面面临资源和技术的双重…

项目经理催开发进度的5大技巧

项目经理在催促开发进度时&#xff0c;采用的技巧对于项目成功至关重要。这不仅影响到项目的按时完成&#xff0c;还关系到团队的士气、效率和最终产品的质量。项目经理如果不去主动催促开发进度&#xff0c;可能导致项目延期&#xff0c;团队偏离原定路线&#xff0c;工作重点…

ASTRONEER异星探险家服务器开服多人联机

1、购买后登录服务器 进入控制面板后会出现正在安装的界面&#xff0c;安装大约5分钟&#xff08;如长时间处于安装中请联系我们的客服人员&#xff09; 2、获取数字IP地址 使用IP 地址查询网站将服务器IP地址填入后点击查询&#xff08;查询的时候将地址冒号后方的数字以及冒…

红苹果的圆度、直径大小标定

function fruitImageProcessingGUI% 创建主窗口和控件mainFigure figure(Units, normalized, Position, [0.3, 0.3, 0.4, 0.4]);instructionText uicontrol(Style, text, String, 请点击按钮执行相应的图像处理步骤, ...Units, normalized, Position, [0.1, 0.7, 0.8, 0.2], …

Nacos 进阶篇---Nacos服务下线做了哪些事情 ?(八)

一、引言 本章节是第一阶段最后一篇&#xff0c;那么我们今天要学习的源码内容是 “服务下线”. 当Nacos客户端下线的时候&#xff0c;是要去通知服务端&#xff0c;告诉服务端 “ 我已经下线&#xff0c;不可用了 ”。并且在服务下线时&#xff0c;还要去通知其他客户端服务更…

uni-app解决表格uni-table样式问题

一、如何让表格文字只显示一行&#xff0c;超出部分用省略号表示 步骤 &#xff1a; 给table设置table-layout:fixed; 列宽由表格宽度和列宽度设定。&#xff08;默认是由单元格内容设定&#xff09;让表格元素继承父元素宽度固定table-layout: inherit;overflow: hidden;超过…

STM32-电灯,仿真

目录 1.配置vscode 2.新创建软件工程 3.仿真 4.源码 5.运行效果 1.配置vscode http://t.csdnimg.cn/BvCLx 安装 C/C Extension Pack 安装 Embedded IDE 安装 Keil MDK 配置路径 2.新创建软件工程 下拉找到对应的 输入项目名字,选择项目所在文件夹即可 3.仿真 一路新…

机器视觉分析在加油站安全中的应用:使用手机检测、打电话行为识别

在加油站等高危场所&#xff0c;禁止使用手机是为了防止潜在的火灾和爆炸风险。手机在使用过程中可能产生电火花&#xff0c;而在加油站这种易燃易爆环境中&#xff0c;任何电火花都可能引发严重的安全事故。因此&#xff0c;加油站禁止使用手机是保障安全生产的重要措施。基于…

【好书分享第十三期】AI数据处理实战108招:ChatGPT+Excel+VBA

文章目录 一、内容介绍二、内页插图三、作者简介四、前言/序言五、目录 一、内容介绍 《AI数据处理实战108招&#xff1a;ChatGPTExcelVBA》通过7个专题内容、108个实用技巧&#xff0c;讲解了如何运用ChatGPT结合办公软件Excel和VBA代码实现AI办公智能化、高效化。随书附赠了…