You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 21:24:03