Room持久化对象映射技术咨询——附Subject与Grade实体类代码
Hey folks, let's dive deep into how object-relational mapping (ORM) works in Android's Room Persistence Library, using the two entity classes you provided as concrete examples. Room takes the pain out of raw SQLite by mapping your Java objects directly to database tables—so let's break down every annotation and its role in your code.
1. 基础实体映射:@Entity 注解
First up, the @Entity annotation marks a class as a database table. Every instance of this class will correspond to a row in the table.
Looking at your Subject.java:
@Entity(tableName = Constants.SUBJECT_ENTITY) public class Subject { // ... }
tableName: Explicitly sets the name of the database table. Using a constant (likeConstants.SUBJECT_ENTITY) instead of a hardcoded string makes your code easier to maintain—if you ever need to rename the table, you only change it in one place.
Your Grade.java uses the same pattern, plus an extra foreignKeys parameter we'll cover next:
@Entity(tableName = Constants.GRADE_ENTITY, foreignKeys = @ForeignKey(...)) public class Grade implements Serializable { // ... }
2. 主键定义:@PrimaryKey 注解
Every database table needs a primary key to uniquely identify rows—Room enforces this with the @PrimaryKey annotation.
In both your entities:
@PrimaryKey private int id;
- This maps the
idfield to the primary key column of the table. - Note: If you want SQLite to auto-generate unique IDs for new rows, add
autoGenerate = trueto the annotation:@PrimaryKey(autoGenerate = true). Your current code assumes you'll manually set theidvalue when creating instances.
3. 列名自定义:@ColumnInfo 注解
By default, Room uses the field name as the column name in the table. But if you want a different column name (for consistency with existing database schemas, or to avoid naming conflicts), use @ColumnInfo.
In Subject.java:
@ColumnInfo(name = "name") private String name;
Here, the field name maps to a column named name (which matches, but it's still a good practice to use the annotation explicitly). For a more practical example, if your field was subjectName, you could use @ColumnInfo(name = "name") to map it to a shorter column name.
In Grade.java, you apply the same logic to the semester field:
@ColumnInfo(name = "semester") private int semester;
4. 表关联:@ForeignKey 注解
This is where things get interesting—Room uses @ForeignKey to establish relationships between tables. Your Grade entity is linked to Subject because a grade belongs to a specific subject.
Let's unpack the @ForeignKey in your Grade.java:
foreignKeys = @ForeignKey( entity = Subject.class, onDelete = CASCADE, parentColumns = "id", childColumns = "subjectId" )
Let's break down each parameter:
entity = Subject.class: Specifies the parent entity (the table we're linking to).parentColumns = "id": The column in the parent table (Subject) that acts as the primary key for the relationship.childColumns = "subjectId": The column in the child table (Grade) that stores the foreign key value (matching theidof aSubject). You'll need to add this field to yourGradeclass (I've included it in the full code snippet below).onDelete = CASCADE: A critical constraint—this means if aSubjectis deleted from the database, all linkedGraderecords will be automatically deleted too. This prevents "orphaned" grade entries that don't belong to any subject.
Here's the complete Grade class with the missing subjectId field:
@Entity(tableName = Constants.GRADE_ENTITY, foreignKeys = @ForeignKey( entity = Subject.class, onDelete = CASCADE, parentColumns = "id", childColumns = "subjectId")) public class Grade implements Serializable { @PrimaryKey private int id; @ColumnInfo(name = "semester") private int semester; @ColumnInfo(name = "subjectId") private int subjectId; // Foreign key linking to Subject's id // Constructor, getters, and setters omitted for brevity }
Bonus: Fetching Related Data with @Relation
If you want to fetch a Subject along with all its associated Grade records in a single query, you can create a wrapper class with the @Relation annotation:
public class SubjectWithGrades { @Embedded // Embeds the Subject fields directly into the result private Subject subject; @Relation( parentColumn = "id", entityColumn = "subjectId" ) private List<Grade> grades; // List of linked Grade records }
You can then define a DAO method to query this:
@Query("SELECT * FROM subject") List<SubjectWithGrades> getSubjectsWithGrades();
Key Takeaways
@Entityturns a class into a database table.@PrimaryKeydefines the unique identifier for rows.@ColumnInfolets you customize column names.@ForeignKeyestablishes relationships between tables and enforces constraints like cascading deletes.@Relationsimplifies fetching related data in one go.
Hope this breakdown clarifies how Room maps your objects to database tables and handles relationships. Feel free to ask follow-up questions about DAOs, query optimization, or more advanced ORM features!
内容的提问来源于stack exchange,提问作者Kamil Grosicki

