## 2. Room数据库核心组件:Entity、DAO、Database、TypeConverter,配置与初始化 ### 2.1 组件全景与职责 Room 是 Android 官方推荐的持久化库,建立在 SQLite 之上。其核心架构由四个组件构成,每个组件承担独立的职责: | 组件 | 职责 | 备注 | |------|------|------| | **Entity** | 定义数据库表结构,每个 Entity 对应一张表 | 字段映射列,@PrimaryKey 标记主键 | | **DAO** (Data Access Object) | 封装所有数据操作(增删改查) | 接口或抽象类,使用注解标记 SQL | | **Database** | 数据库持有者,提供 DAO 实例,管理版本与迁移 | 单例,继承 RoomDatabase | | **TypeConverter** | 自定义类型转换,使 Room 能存储复杂对象(如 Date、List) | 静态方法,@TypeConverter 注解 | 四个组件的协作关系如下:
Room 核心组件协作关系 RoomDatabase 数据库持有者(单例) Entity 表结构定义 DAO 数据操作接口 TypeConverter 类型转换器 SQLite 表 → 列 SQL 操作 类型映射 底层引擎
### 2.2 各组件实现流程 #### 2.2.1 Entity — 数据表定义 每个 Entity 类使用 `@Entity` 注解,字段与表列一一对应。必须指定主键,支持自动生成。
@Entity(tableName = "ble_device")
public class BleDeviceEntity {
    @PrimaryKey(autoGenerate = true)
    private int id;
    @NonNull
    @ColumnInfo(name = "device_address")
    private String address;
    private String name;
    private int rssi;
    @ColumnInfo(name = "last_seen")
    private long lastSeenTimestamp;
    // 构造函数、Getter、Setter 省略
}
最佳实践:Entity 字段尽量使用基本类型 + String,避免复杂嵌套对象。若必须存储,需配合 TypeConverter。
#### 2.2.2 DAO — 数据操作接口 DAO 是接口或抽象类,使用 `@Dao` 注解。方法上标注 `@Insert`、`@Update`、`@Delete`、`@Query` 等。
@Dao
public interface BleDeviceDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(BleDeviceEntity device);

    @Update
    void update(BleDeviceEntity device);

    @Delete
    void delete(BleDeviceEntity device);

    @Query("SELECT * FROM ble_device ORDER BY last_seen DESC")
    LiveData<List<BleDeviceEntity>> getAllDevices();

    @Query("SELECT * FROM ble_device WHERE device_address = :address")
    BleDeviceEntity getDeviceByAddress(String address);
}
#### 2.2.3 TypeConverter — 自定义类型转换 Room 默认支持基本类型、String、ByteArray。若需存储 Date、List 等,需编写转换器。
public class Converters {
    @TypeConverter
    public static Date fromTimestamp(Long value) {
        return value == null ? null : new Date(value);
    }

    @TypeConverter
    public static Long dateToTimestamp(Date date) {
        return date == null ? null : date.getTime();
    }

    @TypeConverter
    public static List<String> fromString(String value) {
        if (value == null) return null;
        return Arrays.asList(value.split(","));
    }

    @TypeConverter
    public static String fromList(List<String> list) {
        if (list == null) return null;
        return String.join(",", list);
    }
}
#### 2.2.4 Database — 数据库定义与初始化 数据库类继承 `RoomDatabase`,使用 `@Database` 注解声明 entities、version,并绑定 TypeConverters。
@Database(
    entities = {BleDeviceEntity.class},
    version = 1,
    exportSchema = false
)
@TypeConverters(Converters.class)
public abstract class BleDeviceDatabase extends RoomDatabase {
    public abstract BleDeviceDao bleDeviceDao();

    private static volatile BleDeviceDatabase INSTANCE;

    public static BleDeviceDatabase getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (BleDeviceDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(
                        context.getApplicationContext(),
                        BleDeviceDatabase.class,
                        "ble_device_db"
                    )
                    .fallbackToDestructiveMigration()
                    .build();
                }
            }
        }
        return INSTANCE;
    }
}
### 2.3 配置与初始化要点 | 配置项 | 说明 | 推荐值 | |--------|------|--------| | `entities` | 声明所有 Entity 类 | 数组形式,如 `{BleDeviceEntity.class}` | | `version` | 数据库版本号,升级时递增 | 起始 1 | | `exportSchema` | 是否导出 Schema 到文件 | 调试阶段设为 true,发布 false | | `@TypeConverters` | 关联自定义转换器类 | 作用于 Database 类 | | `.fallbackToDestructiveMigration()` | 版本不匹配时直接删除重建 | 开发阶段使用,生产环境需谨慎 | | `.addMigrations(Migration... migration)` | 定义迁移策略 | 生产环境推荐 | ### 2.4 调试与验证 **检查生成的 DAO 实现** 可查看 `build/generated/source/apt/debug/` 目录下的文件。 **常见错误排查:**
典型错误 1:Entity 缺少无参构造函数或主键 —— 编译报错 “Entities must have a primary key”。 典型错误 2:TypeConverter 未注册,存储 Date 时出现 “Cannot figure out how to save this field into database”。 典型错误 3:Database 未使用单例模式,造成多次打开数据库连接 — 使用 volatile + synchronized 解决。
### 2.5 性能与稳定性优化 - **DAO 方法使用 `LiveData` 或 `Flow`**,实现自动通知 UI 更新,避免手动刷新。 - **事务操作** 在 DAO 方法上添加 `@Transaction`,保证批量插入/更新的原子性。 - **索引优化**:在查询频繁的字段上添加 `@Index`,例如 `@Entity(indices = {@Index("device_address")})`。 - **预编译 WAL 模式**:Room 默认启用 WAL(Write-Ahead Logging),提高并发读写性能。无需手动配置。 - **迁移策略**:生产环境必须使用 `addMigrations()` 逐步迁移,避免 `fallbackToDestructiveMigration()` 造成数据丢失。
// 迁移示例:从版本 1 升级到 2
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE ble_device ADD COLUMN battery_level INTEGER DEFAULT 0");
    }
};
### 2.6 组件集成验证清单 - [ ] Entity 已添加 `@Entity` 并指定主键 - [ ] DAO 接口已标注 `@Dao`,SQL 语法正确 - [ ] TypeConverter 类已编写并用 `@TypeConverters` 关联到 Database - [ ] Database 类继承 `RoomDatabase`,**单例模式**实现 getInstance - [ ] 数据库名称唯一(避免与其他模块冲突) - [ ] Version 号随表结构变化递增 - [ ] 生产环境使用 `addMigrations()` 而非 `fallbackToDestructiveMigration()` - [ ] 性能敏感查询添加索引和 `@Transaction`