Android学习总结之Room篇

一、Room 框架基础

1. 实体类(Entity)

实体类用于描述数据库表的结构。通过使用 @Entity 注解,可以将一个 Java 类映射到数据库中的一张表。例如,以下是一个简单的 User 实体类:

java 复制代码
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity
public class User {
    @PrimaryKey(autoGenerate = true)
    public int id;
    public String name;
    public int age;
}

在这个例子中,@Entity 注解将 User 类标记为数据库实体,@PrimaryKey 注解指定了 id 字段为主键,并且 autoGenerate = true 表示主键将自动生成。

2. 数据访问对象(DAO)

DAO 接口定义了对数据库的操作方法。通过使用 @Query@Insert@Update@Delete 等注解,可以方便地执行 SQL 查询和数据的增删改操作。以下是一个简单的 UserDao 接口示例:

java 复制代码
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;

import java.util.List;

@Dao
public interface UserDao {
    @Insert
    void insertUser(User user);

    @Query("SELECT * FROM User")
    List<User> getAllUsers();
}

在这个例子中,@Insert 注解用于插入数据,@Query 注解用于执行 SQL 查询语句。

3. 数据库类(Database)

数据库类负责创建和管理数据库实例。通过继承 RoomDatabase 类,并使用 @Database 注解指定实体类和数据库版本,可以创建一个 Room 数据库。以下是一个简单的 AppDatabase 类示例:

java 复制代码
import androidx.room.Database;
import androidx.room.RoomDatabase;

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

在这个例子中,@Database 注解指定了 User 类为数据库实体,版本号为 1。userDao() 方法用于获取 UserDao 实例。

二、Room 中实现 Entity 嵌套 Entity 创建相应的表

1. 定义相关实体类

假设我们有一个Author(作者)实体类和一个Book(书籍)实体类,每一个Author可以有多本Book,我们希望在查询Author时能同时获取其相关的Book信息。

Author实体类
java 复制代码
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import java.util.List;

@Entity
public class Author {
    @PrimaryKey
    public int id;
    public String name;
    // 这里的books是与该作者相关的书籍列表,在后续会通过@Relation注解关联
    public List<Book> books;
}
Book实体类
java 复制代码
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.PrimaryKey;

@Entity(foreignKeys = @ForeignKey(entity = Author.class,
        parentColumns = "id",
        childColumns = "authorId"))
public class Book {
    @PrimaryKey
    public int id;
    public String title;
    // 关联作者的id
    public int authorId;
}

2. 定义包含关系的 POJO(Plain Old Java Object)类

为了更好地处理嵌套关系,我们创建一个 POJO 类,用于将Author和其相关的Book进行组合。

java 复制代码
import androidx.room.Embedded;
import androidx.room.Relation;
import java.util.List;

public class AuthorWithBooks {
    // 将Author实体类嵌入到AuthorWithBooks中
    @Embedded
    public Author author;
    // 通过外键关联,获取与该作者相关的书籍列表
    @Relation(
            parentColumn = "id",
            entityColumn = "authorId"
    )
    public List<Book> books;
}

在上述代码中:

  • @Embedded注解用于将一个实体类嵌入到另一个类中,这里将Author实体嵌入到AuthorWithBooks中。
  • @Relation注解用于定义两个实体之间的关系,parentColumn指定父实体(这里是Author)的列,entityColumn指定子实体(这里是Book)的列,通过这两个列的关联来获取相关数据。

3. 定义数据访问对象(DAO)接口

在 DAO 接口中定义查询方法,用于获取AuthorWithBooks数据。

java 复制代码
import androidx.room.Dao;
import androidx.room.Query;

import java.util.List;

@Dao
public interface AuthorDao {
    @Query("SELECT * FROM Author")
    List<AuthorWithBooks> getAuthorsWithBooks();
}

这里的查询语句只是简单地从Author表中查询数据,Room 会根据AuthorWithBooks类中的关系注解,自动关联并获取相关的Book数据。

4. 定义 Room 数据库类

java 复制代码
import androidx.room.Database;
import androidx.room.RoomDatabase;

@Database(entities = {Author.class, Book.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract AuthorDao authorDao();
}

5. 使用示例

java 复制代码
import androidx.room.Room;

public class MainActivity {
    public static void main(String[] args) {
        AppDatabase db = Room.databaseBuilder(
               , AppDatabase.class, "my - database")
               .build();
        AuthorDao authorDao = db.authorDao();
        List<AuthorWithBooks> authorsWithBooks = authorDao.getAuthorsWithBooks();
        for (AuthorWithBooks authorWithBooks : authorsWithBooks) {
            System.out.println("作者:" + authorWithBooks.author.name);
            for (Book book : authorWithBooks.books) {
                System.out.println("    书籍:" + book.title);
            }
        }
    }
}

三、Room的多表查询

1. 多表查询 SQL 语句示例(以常见的 MySQL 为例,假设存在学生表 students、课程表 courses、成绩表 scores)

假设 students 表有字段 id(学生编号)、name(学生姓名);courses 表有字段 id(课程编号)、course_name(课程名称);scores 表有字段 id(成绩记录编号)、student_id(关联学生表的学生编号)、course_id(关联课程表的课程编号)、score(成绩) 。

要查询每个学生的姓名、所选课程名称以及对应的成绩,SQL 语句如下:

sql 复制代码
SELECT 
    s.name,
    c.course_name,
    sc.score
FROM 
    students s
-- 使用INNER JOIN(内连接)关联成绩表scores
JOIN 
    scores sc ON s.id = sc.student_id
-- 使用INNER JOIN关联课程表courses
JOIN 
    courses c ON sc.course_id = c.id;

解释:

  • SELECT 子句指定要查询的列,这里选择学生姓名 s.name、课程名称 c.course_name 和成绩 sc.score
  • FROM 子句指定主表,这里以 students 表为主表,起别名 s
  • JOIN 用于连接其他表,ON 子句指定连接条件。第一个 JOINstudents 表和 scores 表通过学生编号关联起来;第二个 JOINscores 表和 courses 表通过课程编号关联起来,从而实现多表数据的联合查询。

2. 使用 Room(Android 中的数据库框架)实现类似多表查询

首先,定义相关的实体类:

  • 学生实体类 Student
java 复制代码
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity
public class Student {
    @PrimaryKey
    public int id;
    public String name;
}
  • 课程实体类 Course
java 复制代码
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity
public class Course {
    @PrimaryKey
    public int id;
    public String course_name;
}
  • 成绩实体类 Score
java 复制代码
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.PrimaryKey;

@Entity(foreignKeys = {
        @ForeignKey(entity = Student.class, parentColumns = "id", childColumns = "student_id"),
        @ForeignKey(entity = Course.class, parentColumns = "id", childColumns = "course_id")
})
public class Score {
    @PrimaryKey
    public int id;
    public int student_id;
    public int course_id;
    public int score;
}

然后,定义数据访问对象(DAO)接口,在其中定义查询方法:

java 复制代码
import androidx.room.Dao;
import androidx.room.Query;

import java.util.List;

@Dao
public interface ScoreDao {
    @Query("SELECT s.name, c.course_name, sc.score " +
           "FROM Student s " +
           "JOIN Score sc ON s.id = sc.student_id " +
           "JOIN Course c ON sc.course_id = c.id")
    List<Object[]> getStudentCourseScore();
}

这里的 @Query 注解中写的 SQL 语句和前面纯 SQL 的多表查询思路一致。由于查询结果涉及多个表的不同列,返回类型定义为 List<Object[]>,每一个 Object[] 数组代表一条查询结果记录,数组中的元素依次对应查询的列(学生姓名、课程名称、成绩) 。

在使用时,通过 Room 数据库实例获取 ScoreDao 的实例并调用该查询方法:

java 复制代码
import androidx.room.Room;

public class MainActivity {
    public static void main(String[] args) {
        AppDatabase db = Room.databaseBuilder(
               , AppDatabase.class, "my - database")
               .build();
        ScoreDao scoreDao = db.scoreDao();
        List<Object[]> result = scoreDao.getStudentCourseScore();
        for (Object[] row : result) {
            String studentName = (String) row[0];
            String courseName = (String) row[1];
            int studentScore = (int) row[2];
            System.out.println("学生:" + studentName + ",课程:" + courseName + ",成绩:" + studentScore);
        }
    }
}

四、Room 与直接使用 SQLite 的区别

易用性

  • Room :作为 Google 官方提供的在 Android 平台上操作数据库的抽象层框架,它对 SQLite 进行了封装,使用起来更加简洁和直观。例如,定义实体类时,只需使用注解(如@Entity@PrimaryKey等)来标识表结构和主键等信息,无需手动编写复杂的建表 SQL 语句;在数据访问对象(DAO)中,通过@Query注解编写 SQL 语句,或者使用框架提供的一些内置方法(如insertupdatedelete等)来操作数据库,大大简化了开发流程。
  • SQLite :直接使用 SQLite 时,需要开发者手动编写各种 SQL 语句来完成建表、插入、查询、更新和删除等操作,例如创建表时要完整地编写CREATE TABLE语句,包含列名、数据类型、约束条件等,相对来说对开发者的 SQL 知识要求较高,代码编写也更为繁琐。

代码结构

  • Room:有助于构建清晰、可维护的代码结构。它将数据库操作相关的代码划分为实体类(Entity)、数据访问对象(DAO)和数据库类(Database)。实体类用于描述数据库表中的数据结构,DAO 接口定义了对数据库的操作方法,数据库类则负责创建和管理数据库实例。这种分层的设计使得代码职责明确,易于理解和维护,符合现代 Android 开发的架构规范。
  • SQLite:直接使用 SQLite 时,代码结构相对较为松散,开发者可能会将 SQL 语句分散在不同的业务逻辑代码中,随着项目的增大,代码的可读性和可维护性会逐渐降低,难以快速定位和修改数据库相关的代码。

功能特性

  • Room :提供了许多实用的功能特性。比如,支持数据库版本管理,当数据库结构发生变化时,可以方便地进行版本升级,并在升级过程中执行相应的迁移操作;还支持关系映射,通过@Relation注解可以方便地处理表与表之间的关联关系,实现多表查询和数据的嵌套加载,如在一个实体类中嵌套另一个实体类的集合。
  • SQLite:本身是一个轻量级的数据库引擎,仅提供基本的数据库操作功能,对于数据库版本管理和复杂的关系映射等功能,需要开发者自行编写大量的代码来实现,增加了开发的难度和工作量。

性能

  • Room:虽然对 SQLite 进行了封装,但在执行 SQL 语句时,底层仍然是通过 SQLite 来实现的,因此在性能上不会有太大的损耗。同时,Room 还会对一些常见的操作进行优化,例如在批量插入数据时,会采用合适的方式来提高插入效率。不过,由于 Room 需要进行一些额外的处理(如注解处理、对象映射等),在某些极端情况下,可能会有轻微的性能开销。
  • SQLite:由于直接操作数据库,没有中间层的额外处理,在一些对性能要求极高、对代码执行效率非常敏感的场景下,直接使用 SQLite 可能会具有一定的性能优势,可以根据具体需求进行更精细的性能优化。

安全性

  • Room:在一定程度上增强了数据库操作的安全性。例如,在执行 SQL 语句时,Room 会对输入参数进行检查和处理,避免 SQL 注入等安全问题。通过使用类型安全的方法和参数绑定,减少了因错误的 SQL 语句导致的数据泄露或损坏的风险。
  • SQLite:直接使用 SQLite 时,如果开发者在编写 SQL 语句时不注意对输入参数的处理,很容易出现 SQL 注入漏洞,攻击者可以通过构造恶意的 SQL 语句来获取敏感数据或破坏数据库,安全性相对较低,需要开发者具备较强的安全意识和编写安全代码的能力。

五、总结

Room 框架为 Android 开发者提供了一种更加优雅、高效的数据库操作方式。通过使用注解和分层设计,它简化了数据库操作的代码编写,提高了代码的可读性和可维护性。

相关推荐
张小潇21 小时前
AOSP15 Input专题InputDispatcher源码分析
android
TT_Close21 小时前
【Flutter×鸿蒙】debug 包也要签名,这点和 Android 差远了
android·flutter·harmonyos
Kapaseker1 天前
2026年,我们还该不该学编程?
android·kotlin
雨白2 天前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk2 天前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING2 天前
RN容器启动优化实践
android·react native
恋猫de小郭2 天前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker2 天前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴2 天前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭2 天前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter