Room Database存储嵌套列表JSON时Entity报错,如何正确定义实体类
错误原因
你遇到的报错核心是@Embedded注解仅支持嵌套单个自定义对象,不支持直接作用于List这类集合类型。Room无法直接将集合数据映射为数据库表的单列结构,因此抛出了找不到List可用构造函数的异常。
解决方案
根据你的使用场景可以选择以下两种实现方式:
方案1:TypeConverter 序列化存储(适合小数据量,无需单独查询子菜单)
通过自定义类型转换器将子菜单列表序列化为JSON字符串存入数据库,读取时自动反序列化为List,是实现成本最低的方案。
步骤1:实现类型转换器
import androidx.room.TypeConverter import com.google.gson.Gson import com.google.gson.reflect.TypeToken class SubmenuListConverter { private val gson = Gson() @TypeConverter fun listToJson(list: List<Submenu>): String { return gson.toJson(list) } @TypeConverter fun jsonToList(json: String): List<Submenu> { val listType = object : TypeToken<List<Submenu>>() {}.type return gson.fromJson(json, listType) } }
步骤2:在数据库类注册转换器
@Database(entities = [MenuEntity::class], version = 1) @TypeConverters(SubmenuListConverter::class) // 注册转换器 abstract class AppDatabase : RoomDatabase() { // 此处定义你的Dao接口 }
步骤3:修正MenuEntity代码
去掉@Embedded注解,同时添加@SerializedName保证JSON字段和实体字段匹配:
import com.google.gson.annotations.SerializedName @Entity(tableName = "menus", primaryKeys = ["name", "type"]) data class MenuEntity( val name: String, val type: String, @SerializedName("submenu") // 匹配JSON中的submenu字段名 val submenus: List<Submenu> )
Submenu类无需修改,该方案下读取JSON后可直接将解析得到的实体插入数据库,查询时也会自动返回带完整子菜单列表的MenuEntity对象。
方案2:分表关联存储(适合大数据量,需单独操作子菜单)
遵循数据库范式,拆分为主菜单、子菜单两张表,通过外键关联,适合需要单独查询/修改子菜单的场景。
步骤1:定义主菜单表
@Entity(tableName = "menus", primaryKeys = ["name", "type"]) data class MenuEntity( val name: String, val type: String )
步骤2:定义子菜单表(带外键关联)
@Entity( tableName = "submenus", primaryKeys = ["parent_menu_name", "parent_menu_type", "submenu_name"], foreignKeys = [ForeignKey( entity = MenuEntity::class, parentColumns = ["name", "type"], childColumns = ["parent_menu_name", "parent_menu_type"], onDelete = ForeignKey.CASCADE // 主菜单删除时自动删除对应子菜单 )] ) data class SubmenuEntity( val parent_menu_name: String, val parent_menu_type: String, val submenu_name: String, val route: String )
步骤3:定义关联查询POJO
data class MenuWithSubmenus( @Embedded val menu: MenuEntity, @Relation( parentColumns = ["name", "type"], entityColumns = ["parent_menu_name", "parent_menu_type"] ) val submenus: List<SubmenuEntity> )
步骤4:Dao层实现
@Dao interface MenuDao { // 插入主菜单 @Insert suspend fun insertMenus(menus: List<MenuEntity>) // 插入子菜单 @Insert suspend fun insertSubmenus(submenus: List<SubmenuEntity>) // 查询带子菜单的全量数据 @Transaction @Query("SELECT * FROM menus") suspend fun getAllMenusWithSubmenus(): List<MenuWithSubmenus> }
该方案下插入数据的逻辑为:解析JSON得到菜单列表 → 先插入所有主菜单数据 → 遍历每个主菜单的子菜单,填充parent_menu_name和parent_menu_type后批量插入子菜单表。
内容的提问来源于stack exchange,提问作者Polaris Nation
相关产品推荐
相关产品推荐

