3. Entity 基础:创建第一个实体类、@Entity 注解、@PrimaryKey、字段命名、表名指定

3.1 本章核心概念

在 Room 中,Entity(实体类) 是数据持久化的最小单元。每个 Entity 对应数据库中的一张表,实体类的每个字段(非静态、非 transient)默认对应表的一列。本章将带领您从零构建第一个 Entity,理解 @Entity、@PrimaryKey 注解的正确用法,并掌握表名、字段命名的最佳实践。

概念说明
@Entity标记一个类为 Room 实体,默认表名为类名(不区分大小写)。
@PrimaryKey标记一个字段为主键,支持自增(autoGenerate)。
字段命名默认使用字段名映射列名,可通过 @ColumnInfo 自定义。
表名指定在 @Entity(tableName = "xxx") 中显式指定。
💡 设计建议:表名使用下划线命名法(如 user_profile),字段名使用驼峰或下划线均可,但建议保持全小写下划线风格以保证数据库兼容性。

下图为 Entity → 数据库表的核心映射关系:

Entity → 数据库表核心映射 Java/Kotlin Entity 类 @Entity(tableName = "user") @PrimaryKey private int id; private String name; private int age; 数据库表 user id INTEGER PRIMARY KEY name TEXT age INTEGER 映射规则说明 • 类名 → 表名(默认),可通过 tableName 自定义 • 字段名 → 列名,可通过 @ColumnInfo(name = "...") 覆盖 • 所有非 static、非 transient 字段自动加入表结构

3.2 创建第一个实体类

以用户信息表为例,演示完整的 Entity 定义步骤。

3.2.1 添加依赖

确保 build.gradle(模块级)中已包含 Room 依赖:

// build.gradle (模块级)
dependencies {
    def room_version = "2.6.1"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"  // Java 使用
    // 如果使用 Kotlin: kapt "androidx.room:room-compiler:$room_version"
}

3.2.2 编写实体类

// User.java
package com.example.app.data.entity;

import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "user")
public class User {

    @PrimaryKey(autoGenerate = true)
    private int id;

    private String name;
    private int age;

    // 构造方法(Room 需要无参构造或全参构造)
    public User() {}

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter / Setter(必须,Room 通过反射读写字段)
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}
⚠️ 重要规则:Room 实体类必须暴露每个持久化字段的 Getter 和 Setter(或使用 Kotlin 属性)。如果字段为 private 但没有 Getter/Setter,Room 会编译时报错“Cannot find getter for field”。

3.3 @Entity 注解详解

@Entity 注解位于类声明之上,可选的配置参数:

参数类型默认值说明
tableNameString类名(小写)指定数据库表名,推荐使用下划线命名
indicesIndex[]空数组定义索引,提高查询性能
primaryKeysString[]由 @PrimaryKey 决定定义复合主键(多个字段组合)
foreignKeysForeignKey[]空数组定义外键约束
ignoredColumnsString[]空数组忽略某些字段,不映射到数据库列
💡 建议:始终显式指定 tableName,避免因类名重构导致表名意外变化。例如:@Entity(tableName = "user_info")

3.4 @PrimaryKey 的使用

@PrimaryKey 用于标记实体中的主键字段。常用两种模式:

  1. 手动赋值:不设置 autoGenerate,由程序员在插入前手动设置 id 值。
  2. 自动生成:设置 autoGenerate = true,Room 在插入时自动分配整数自增值。
@PrimaryKey(autoGenerate = true)
private int id;  // 推荐:数据库自动递增
⚠️ 注意:autoGenerate 仅对整数类型(int / Long)有效。对于 String 类型主键,必须手动赋值且不能 autoGenerate。

3.5 字段命名与列名映射

默认情况下,字段名就是列名。如果需要定制列名,使用 @ColumnInfo:

import androidx.room.ColumnInfo;

public class User {
    @PrimaryKey(autoGenerate = true)
    private int id;

    @ColumnInfo(name = "full_name")
    private String name;

    @ColumnInfo(name = "user_age")
    private int age;
}

字段命名常见风格对比:

风格Java 示例数据库列名推荐度
驼峰private String userName;userName中等(SQL 中不区分大小写,易混淆)
下划线private String user_name;user_name推荐(数据库习惯,可读性高)
@ColumnInfo 映射@ColumnInfo(name = "user_name")
private String userName;
user_name最佳(代码保持驼峰,表用下划线)

3.6 实现流程归纳

  1. 定义包结构:通常将 Entity 类放在 data/entity 包下。
  2. 编写实体类:添加 @Entity 注解,设置 tableName。
  3. 声明主键:添加 @PrimaryKey 并决定是否 autoGenerate。
  4. 定义字段和 Getter/Setter:所有持久化字段必须提供访问方法。
  5. 编译验证:执行 Build → Make Project,若注解处理成功则无报错。

3.7 常见错误与调试

错误信息原因解决方法
Cannot find getter for field 'xxx'缺少 Getter 方法添加对应 getXxx() 方法
Entities and Pojos must have a usable public constructor缺少无参构造添加 public User() {} 或使用全参构造
Error: @Entity class must have a @PrimaryKey未定义主键至少为⼀个字段添加 @PrimaryKey
Multiple primary keys defined定义了多个 @PrimaryKey(非复合)要么只用一个,要么使用 @Entity(primaryKeys = {}) 指定复合主键

3.8 性能与稳定性建议

  • 主键类型:推荐使用 int 或 long 配合 autoGenerate = true,性能优于 String 类型。
  • 避免冗余字段:Entity 中只声明数据库需要的字段,无关字段用 @Ignore 注解排除(将在后续章节讲解)。
  • 表名冲突:不同 Entity 的 tableName 不能重复,否则编译时报错 Multiple tables with the same name。
📌 关键总结:一个 Entity 类 + @Entity(tableName = "...") + @PrimaryKey + 完整 Getter/Setter = 一张可操作的数据库表。命名规范决定代码可维护性,建议统一使用下划线表名和驼峰字段名加 @ColumnInfo 映射。