&引用;没有这样的专栏“;Android Room和POJO上多个@Embedded字段的SQL错误

&引用;没有这样的专栏“;Android Room和POJO上多个@Embedded字段的SQL错误,android,android-room,Android,Android Room,原始问题位于最底部。我为我的问题创建了一个最小的(非)工作示例,希望更容易阅读。例子是。有一份自述文件描述了这个问题。我在这里粘贴项目的一些部分 数据模型非常简单: Owner <--(1:N)-- Child --(N:1)--> ReferencedByChild 构建此代码导致此错误消息: error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no suc

原始问题位于最底部。我为我的问题创建了一个最小的(非)工作示例,希望更容易阅读。例子是。有一份自述文件描述了这个问题。我在这里粘贴项目的一些部分

数据模型非常简单:

Owner <--(1:N)-- Child --(N:1)--> ReferencedByChild
构建此代码导致此错误消息:

error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: refByChildId)
我认为
Owner
查询构造得很糟糕,但我不能完全确定。如果这就是问题所在,那么构造查询的正确方法是什么




这是原始问题 我有一个嵌套的POJO结构,它应该表示一个
游戏
有多个
回合
,每个
回合
都有一个与之关联的
主题

class GameWithRounds {
    @Embedded
    var game: Game? = null

    @Relation(
        parentColumn = "id",
        entityColumn = "gameId",
        entity = RoundRoom::class
    )
    var rounds: List<RoundWithTopic>? = null
}

class RoundWithTopic(
    @Embedded
    var round: RoundRoom,

    @Embedded(prefix = "topic_")
    var topic: Topic
)
但是,构建项目会给我带来房间错误:

There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: topic_id)
即使当我提出关于哪些字段实际存在的警告时,Room告诉我:

Columns returned by the query: topic_id, topic_name, topic_description, topic_language, topic_keywords, topic_sourceUrls, topic_furtherUrls, topic_questions, order, gameId, topicId, status, id. Fields in cz.melkamar.sklapecka.model.RoundWithTopic: order, gameId, topicId, status, id, topic_id, topic_name, topic_description, topic_language, topic_keywords, topic_sourceUrls, topic_furtherUrls, topic_questions, topic_image.
查询结果中有
topic\u id
列!为什么我会犯这个错误


为完整起见,这是实体:

@Entity
data class Game(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,

    @Embedded
    val gameConfigurationEmbed: GameConfigurationEmbed
)

data class GameConfigurationEmbed(
    var secondsPerTurn: Int,
    var maxSecondsPerTurn: Int,
    var bonusSecondsPerAnswer: Int
)

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Game::class,
            parentColumns = arrayOf("id"),
            childColumns = arrayOf("gameId"),
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = Topic::class,
            parentColumns = arrayOf("id"),
            childColumns = arrayOf("topicId"),
            onDelete = ForeignKey.CASCADE
        )
    ]
)
@TypeConverters(RoomConverters::class)
data class RoundRoom(
    val order: Int,
    var gameId: Long,
    val topicId: String,
    var status: RoundStatus = RoundStatus.CREATED,

    @PrimaryKey(autoGenerate = true)
    val id: Long = 0
) {
    enum class RoundStatus {
        CREATED, UPCOMING, IN_PROGRESS, FINISHED
    }
}

@Entity
data class Topic(
    @PrimaryKey val id: String,
    val name: String,
    val description: String,
    val language: String,
    val keywords: List<String>,
    val sourceUrls: List<String>,
    val furtherUrls: List<String>,
    val questions: List<String>,
    val image: ByteArray?
)
@实体
数据类游戏(
@PrimaryKey(自动生成=真)
val id:Long=0,
@嵌入
val gameConfigurationEmbed:gameConfigurationEmbed
)
数据类GameConfigurationEmbedded(
变量secondsPerTurn:Int,
var maxSecondsPerTurn:Int,
var bonusSecondsPerAnswer:Int
)
@实体(
外键=[
外侨(
实体=游戏::类,
parentColumns=arrayOf(“id”),
childColumns=arrayOf(“gameId”),
onDelete=ForeignKey.CASCADE
),
外侨(
实体=主题::类,
parentColumns=arrayOf(“id”),
childColumns=arrayOf(“topicId”),
onDelete=ForeignKey.CASCADE
)
]
)
@类型转换器(RoomConverters::class)
数据类会议室(
val顺序:Int,
配子体:长,
val-topicId:String,
变量状态:RoundStatus=RoundStatus.CREATED,
@PrimaryKey(自动生成=真)
val id:Long=0
) {
枚举类状态{
已创建、即将发布、正在进行、已完成
}
}
@实体
数据类主题(
@PrimaryKey值id:字符串,
val name:String,
val说明:字符串,
val语言:String,
val关键字:列表,
val sourceurl:List,
val:url列表,
val问题:列表,
val图像:ByteArray?
)

经过一些研究,特别是查看以下链接: 我们找到的唯一答案是

  • 或者创建一个手工生成的sql查询来处理这种情况 在那里你有一个多对多的关系

  • 或者有一个附加的连接实体 随着其余对象的更新而更新。使用这种方法,您可以获取ID,然后根据需要创建其他查询


  • 经过一些研究,特别是查看此链接: 我们找到的唯一答案是

  • 或者创建一个手工生成的sql查询来处理这种情况 在那里你有一个多对多的关系

  • 或者有一个附加的连接实体 随着其余对象的更新而更新。使用这种方法,您可以获取ID,然后根据需要创建其他查询


  • 似乎嵌入式字段和类型转换器在观察问题时没有正确使用。我不想详细讨论这个问题的解决方案,因为它试图使用复杂的关系,我无法在我的机器上测试它的复制

    但我想提供有关使用嵌入式字段和类型转换器的见解

    让我们以上面的问题为例: 游戏表中有字段
    id、secondsPerTurn、maxSecondsPerTurn、bonusSecondsPerAnswer

    可以创建如下所示的实体

    @Entity
    data class Game(
        @PrimaryKey(autoGenerate = true)
        val id: Long = 0,
    
        @Embedded
        val gameConfigurationEmbed: GameConfigurationEmbed
    )
    
    data class GameConfigurationEmbed(
        var secondsPerTurn: Int,
        var maxSecondsPerTurn: Int,
        var bonusSecondsPerAnswer: Int
    )
    
    在SQLite表中,数据实际上存储在四个不同的列中,但是Room基于数据类结构执行CRUD操作,这为开发人员提供了更高的可行性

    类型转换器

    如果我们想要存储非基本数据类型或
    @嵌入式
    不包含的相同类型的数据,类型转换器将非常有用

    例如,足球比赛可以在两个地方举行:主场和客场。Home和Away可以具有相同的字段名,如
    地名、纬度、经度
    。在这种情况下,我们可以创建数据类和类型转换器,如下所示:

    data class GamePlace(
        val placeName:String,
        val latitude:String,
        val longitude:String
    )
    
    @Entity
    data class Game(
        @PrimaryKey(autoGenerate = true)
        val id: Long = 0,
    
        @Embedded
        val gameConfigurationEmbed: GameConfigurationEmbed
    
        @TypeConverters
        var home: GamePlace? = null,
    
        @TypeConverters
        var away: GamePlace? = null,
    )
    
    object Converters {
    
        private val gson = Gson()
    
        @TypeConverter
        @JvmStatic
        fun fromGamePlace(gamePlace: GamePlace?): String? {
            return if (gamePlace == null) null else gson.toJson(gamePlace)
        }
    
        @TypeConverter
        @JvmStatic
        fun toGamePlace(jsonData: String?): GamePlace? {
            return if (jsonData.isNullOrEmpty()) null
            else gson.fromJson(jsonData, object : TypeToken<GamePlace?>() {}.type)
        }
    }
    

    我希望这将有助于处理和解决这个问题。

    在观察这个问题时,似乎没有正确使用嵌入式字段和类型转换器。我不想详细讨论这个问题的解决方案,因为它试图使用复杂的关系,我无法在我的机器上测试它的复制

    但我想提供有关使用嵌入式字段和类型转换器的见解

    让我们以上面的问题为例: 游戏表中有字段
    id、secondsPerTurn、maxSecondsPerTurn、bonusSecondsPerAnswer

    可以创建如下所示的实体

    @Entity
    data class Game(
        @PrimaryKey(autoGenerate = true)
        val id: Long = 0,
    
        @Embedded
        val gameConfigurationEmbed: GameConfigurationEmbed
    )
    
    data class GameConfigurationEmbed(
        var secondsPerTurn: Int,
        var maxSecondsPerTurn: Int,
        var bonusSecondsPerAnswer: Int
    )
    
    在SQLite表中,数据实际上存储在四个不同的列中,但是Room基于数据类结构执行CRUD操作,这为开发人员提供了更高的可行性

    类型转换器

    如果我们想要存储非基本数据类型或
    @嵌入式
    不包含的相同类型的数据,类型转换器将非常有用

    例如,足球比赛可以在两个地方举行:主场和客场。Home和Away可以具有相同的字段名,如
    地名、纬度、经度
    。在这种情况下,我们可以创建数据类和类型转换器,如下所示:

    data class GamePlace(
        val placeName:String,
        val latitude:String,
        val longitude:String
    )
    
    @Entity
    data class Game(
        @PrimaryKey(autoGenerate = true)
        val id: Long = 0,
    
        @Embedded
        val gameConfigurationEmbed: GameConfigurationEmbed
    
        @TypeConverters
        var home: GamePlace? = null,
    
        @TypeConverters
        var away: GamePlace? = null,
    )
    
    object Converters {
    
        private val gson = Gson()
    
        @TypeConverter
        @JvmStatic
        fun fromGamePlace(gamePlace: GamePlace?): String? {
            return if (gamePlace == null) null else gson.toJson(gamePlace)
        }
    
        @TypeConverter
        @JvmStatic
        fun toGamePlace(jsonData: String?): GamePlace? {
            return if (jsonData.isNullOrEmpty()) null
            else gson.fromJson(jsonData, object : TypeToken<GamePlace?>() {}.type)
        }
    }
    
    我希望这将有助于t
    @Database(
        entities = [Game::class /* ,more classes here*/],
        version = 1
    )
    @TypeConverters(Converters::class)
    abstract class AppDatabase : RoomDatabase() {
    
        abstract fun gameDao(): GameDao
    
        //.....
    }