Room框架中实体类关联对象的自动识别与数据保存疑问
嘿,刚好我对Room这块挺熟的,来给你掰扯清楚这个问题~
首先明确说:Room不会自动把你TestModel里引用的Book类当成实体,也不会自动保存关联的Books列表。Room是严格的关系型数据库ORM,和SugarDB那种偏向于NoSQL的存储逻辑不一样,必须显式定义实体和关联规则才行。
下面给你两种可行的解决方案,按需选择:
方案一:用关系型关联(推荐,适合需要单独操作Book的场景)
这种方式符合关系型数据库的设计,能充分利用Room的特性,比如单独查询某个用户的书籍、更新书籍信息等。
1. 给Book类添加@Entity注解并设置外键
因为TestModel和Book是一对多的关系(一个TestModel对应多本Book),所以需要给Book加一个外键字段,关联到TestModel的主键id:
@Entity( foreignKeys = @ForeignKey( entity = TestModel.class, parentColumns = "id", childColumns = "testModelId", onDelete = CASCADE // 可选:当TestModel被删除时,关联的Book也自动删除 ) ) public class Book { @PrimaryKey private int ISBN; // 用ISBN作为Book的主键很合理 private String title; private int testModelId; // 外键,关联TestModel的id // 记得添加getter和setter方法 }
2. 修改TestModel类,去掉直接的List引用
Room实体里不能直接存储对象列表,需要通过@Relation注解在查询时关联数据:
@Entity public class TestModel { @PrimaryKey(autoGenerate = true) private int id; private String name; private String age; // 这里不要直接放List<Book>,关联逻辑放在查询层处理 // 添加getter和setter方法 }
3. 创建关联查询的数据类
需要一个容器类来同时承载TestModel和它关联的Books列表,用@Embedded和@Relation注解实现:
public class TestModelWithBooks { @Embedded // 表示嵌入TestModel的所有字段 public TestModel testModel; @Relation( parentColumn = "id", // TestModel的主键 entityColumn = "testModelId" // Book中关联的外键字段 ) public List<Book> issuedBooks; }
4. 在DAO中编写关联查询和插入方法
@Dao public interface TestModelDao { // 插入TestModel,返回插入后的主键id @Insert long insertTestModel(TestModel testModel); // 批量插入Books @Insert void insertBooks(List<Book> books); // 事务中查询TestModel及其关联的Books,@Transaction保证原子性 @Transaction @Query("SELECT * FROM TestModel") List<TestModelWithBooks> getTestModelsWithBooks(); // 也可以根据id查询单个TestModel的书籍 @Transaction @Query("SELECT * FROM TestModel WHERE id = :testModelId") TestModelWithBooks getTestModelWithBooksById(int testModelId); }
5. 保存数据的步骤
保存时需要先插入TestModel拿到id,再给每个Book设置testModelId,最后插入Books:
// 假设你已经拿到了从接口返回的TestModel对象(包含issuedBooks列表) TestModel testModel = ...; List<Book> books = testModel.getIssuedBooks(); // 插入TestModel,获取生成的id long testModelId = testModelDao.insertTestModel(testModel); // 给每本书设置外键 for (Book book : books) { book.setTestModelId((int) testModelId); } // 插入书籍列表 testModelDao.insertBooks(books);
方案二:用TypeConverter转成JSON存储(适合简单场景,不需要单独操作Book)
如果你的Book数据不需要单独查询、更新,只是作为TestModel的附属数据存在,可以用TypeConverter把List
1. 创建TypeConverter类
利用Gson把List
public class BookListConverter { @TypeConverter public static List<Book> fromJson(String jsonString) { if (jsonString == null) { return new ArrayList<>(); } Type listType = new TypeToken<List<Book>>() {}.getType(); return new Gson().fromJson(jsonString, listType); } @TypeConverter public static String toJson(List<Book> bookList) { Gson gson = new Gson(); return gson.toJson(bookList); } }
2. 在RoomDatabase中注册转换器
@Database(entities = {TestModel.class}, version = 1) @TypeConverters(BookListConverter.class) // 注册转换器 public abstract class AppDatabase extends RoomDatabase { public abstract TestModelDao testModelDao(); }
3. 保留TestModel中的List字段
现在你可以直接在TestModel里保留private List<Book> issuedBooks;,Room会自动把它转成JSON字符串存在数据库里,插入和查询时无需额外处理,和你用SugarDB的方式差不多。
最后总结
- Room不会自动识别关联的实体类,必须给每个需要存储的类添加@Entity注解
- 关联对象的存储有两种方式:关系型关联(推荐复杂场景)和JSON转换(适合简单场景)
- 如果你习惯了SugarDB的便捷,初期可以先用TypeConverter过渡,但长期来看,关系型关联更符合Room的设计理念,也更灵活
内容的提问来源于stack exchange,提问作者Asim

