Android 数据存储(四)-Room

简介: 一、概述1.1 描述 上文介绍到了SQLite Api,今天咱们介绍一下 Room ( Android Jetpack 重要成员之一 )。 Room 持久性库在 SQLite 之上提供了一个抽象层,以允许流畅的数据库访问,同时利用 SQLite 的全部功能。特别是,Room 提供以下好处:SQL 查询的编译时验证。方便的注释,最大限度地减少重复和容易出错的样板代码。简化的数据库迁移路径。 由于这些考虑,我们强烈建议您使用 Room 而不是直接使用 SQLite API。


一、概述


1.1 描述


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


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


  • SQL 查询的编译时验证。


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


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


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


1.2 主要部件


Room 包含三个主要组件:


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


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


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


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

微信图片_20220525135455.png


二、创建 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();

微信图片_20220525140045.png


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


       在构建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();

微信图片_20220525140138.png


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();


微信图片_20220525140208.png


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();

微信图片_20220525140239.png


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());

微信图片_20220525140311.png

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


四、相关链接


Android 数据全方案处理

相关文章
|
18天前
|
消息中间件 网络协议 Java
Android 开发中实现数据传递:广播和Handler
Android 开发中实现数据传递:广播和Handler
16 1
|
4天前
|
Java Linux API
统计android设备的网络数据使用量
统计android设备的网络数据使用量
13 0
|
1天前
|
Android开发 数据库管理
Android如何在Activity和Service之间传递数据
Android如何在Activity和Service之间传递数据
|
1天前
|
XML JSON API
转Android上基于JSON的数据交互应用
转Android上基于JSON的数据交互应用
|
1天前
|
JSON Android开发 数据格式
android与Web服务器交互时的cookie使用-兼谈大众点评数据获得(原创)
android与Web服务器交互时的cookie使用-兼谈大众点评数据获得(原创)
10 2
|
25天前
|
存储 安全 Android开发
Android数据存储:请解释ContentProvider是什么,它的主要作用是什么?
ContentProvider是Android的四大组件之一,主要负责结构化数据的管理与共享。它封装数据并提供安全的访问接口,通过URI实现应用间数据的标准化共享。ContentResolver与ContentProvider协作,处理数据的CRUD操作,使得其他应用能方便地调用和操作数据。
18 0
|
25天前
|
Android开发 开发者
Android网络和数据交互: 请解释Android中的AsyncTask的作用。
Android&#39;s AsyncTask simplifies asynchronous tasks for brief background work, bridging UI and worker threads. It involves execute() for starting tasks, doInBackground() for background execution, publishProgress() for progress updates, and onPostExecute() for returning results to the main thread.
11 0
|
25天前
|
网络协议 安全 API
Android网络和数据交互: 什么是HTTP和HTTPS?在Android中如何进行网络请求?
HTTP和HTTPS是网络数据传输协议,HTTP基于TCP/IP,简单快速,HTTPS则是加密的HTTP,确保数据安全。在Android中,过去常用HttpURLConnection和HttpClient,但HttpClient自Android 6.0起被移除。现在推荐使用支持TLS、流式上传下载、超时配置等特性的HttpsURLConnection进行网络请求。
14 0
|
1月前
|
JSON 安全 Java
Android网络部分-----网络数据请求、解析
Android网络部分-----网络数据请求、解析
Android网络部分-----网络数据请求、解析
|
1月前
|
存储 Android开发
Android数据存储的五种方式3
Android数据存储的五种方式
6 0