Android学习总结之Room篇

一、Room 框架基础

1. 实体类(Entity)

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

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 接口示例:

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 类示例:

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实体类
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实体类
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进行组合。

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数据。

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 数据库类

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. 使用示例

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 语句如下:

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 子句指定连接条件。第一个 JOIN 将 students 表和 scores 表通过学生编号关联起来;第二个 JOIN 将 scores 表和 courses 表通过课程编号关联起来,从而实现多表数据的联合查询。

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

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

  • 学生实体类 Student
import androidx.room.Entity;
import androidx.room.PrimaryKey;

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

@Entity
public class Course {
    @PrimaryKey
    public int id;
    public String course_name;
}
  • 成绩实体类 Score
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)接口,在其中定义查询方法:

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 的实例并调用该查询方法:

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 的核心用处

Room 是 Android Jetpack 组件中的持久化库,通过抽象 SQLite 的底层操作,提供更安全、高效、简洁的数据库访问方式。其核心价值体现在:

  1. 简化数据库操作

    • 注解驱动​:通过 @Entity@Dao@Database 等注解自动生成模板代码。
    • 消除样板代码​:无需手动编写 SQLiteOpenHelperCursor 处理逻辑。
    • 示例对比​:
      // Room 插入数据(1行)
      @Insert
      void insertUser(User user);
      
      // 传统 SQLite 插入数据(约20行)
      // 需手动处理 ContentValues、SQL 语句、异常等
  2. 提升开发效率

    • 编译时校验​:SQL 查询在编译时检查语法错误(传统 SQLite 需运行时调试)。
    • 类型安全​:查询结果直接映射到 Java/Kotlin 对象(无需解析 Cursor)。
    • 关系映射​:通过 @Relation@Embedded 简化嵌套查询(如一对多关系)。
  3. 保障数据安全

    • 防 SQL 注入​:参数化查询自动转义输入值(传统 SQLite 需手动处理)。
    • 事务封装​:通过 @Transaction 注解简化复杂事务操作。
  4. 优化性能

    • 最小化重建​:LiveData + Room 实现数据变化时自动更新 UI(避免全量刷新)。
    • 异步支持​:内置 RxJava/Coroutines 支持,避免主线程阻塞。

二、Room 的核心特性总结
特性Room 的实现方式传统 SQLite 的痛点
实体定义@Entity 注解自动建表需手动编写 CREATE TABLE 语句
数据库操作@Dao 接口声明增删改查方法需手写 ContentValuesCursor
数据库管理@Database 注解自动管理版本升级需重写 onUpgrade() 处理迁移逻辑
关系映射@Relation + @Embedded 实现嵌套查询需手写多表 JOIN 和对象组装逻辑
线程安全强制主线程外操作(否则抛出异常)需手动管理线程切换
类型安全编译时 SQL 校验运行时才能发现 SQL 语法错误

三、Room 的典型使用场景
  1. 复杂数据关系管理

    • 示例:电商应用的「用户-订单-商品」三级嵌套(通过 @Relation 一键查询)。
    @Relation(parentColumn = "userId", entityColumn = "orderUserId")
    List<Order> orders;
  2. 响应式数据流

    • 结合 LiveData 实现数据变动自动刷新 UI:
    @Query("SELECT * FROM User")
    LiveData<List<User>> getAllUsers(); // UI 自动更新
  3. 本地缓存与离线支持

    • 网络数据持久化存储,通过 Repository 模式统一数据源:
    public class UserRepository {
        private UserDao userDao;
        public void saveUser(User user) {
            userDao.insert(user); // 数据库持久化
        }
    }

四、Room vs 直接使用 SQLite 的核心优势
维度Room原生 SQLite
开发效率 注解自动生成代码 需手写大量模板代码
可维护性 清晰的 DAO/Entity 分层结构逻辑分散在 SQLiteOpenHelper
安全性 编译时检查 + 防 SQL 注入 需手动处理参数转义
性能 内置优化(如惰性加载) 依赖开发者优化能力
学习成本 只需掌握注解规则 需深入理解 SQLite API
核心价值​:Room 将 Android 数据库开发从“手工作坊时代”推进到“工业化时代”,是构建健壮、可维护的本地存储方案的首选工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值