简体   繁体   English

Android Room和POJO上的多个@Embedded字段出现“无此列” SQL错误

[英]“no such column” SQL error from Android Room and multiple @Embedded fields on POJO

The original question is at the very bottom. 最初的问题在最底层。 I have created a minimal (non-)working example of my problem which is hopefully easier to read through. 我为我的问题创建了一个最小的(非工作性)示例,希望可以更轻松地阅读。 The example is here at gitlab . 该示例在gitlab上 There is a readme describing the problem. 有描述该问题的自述文件。 I am pasting some parts of the project here. 我在这里粘贴项目的某些部分。

The data model is plain simple: 数据模型非常简单:

Owner <--(1:N)-- Child --(N:1)--> ReferencedByChild

All I want to do is to read an Owner from the database with all its associated Child objects and for each Child object also the ReferencedByChild object that it references. 我要做的就是从数据库中读取一个Owner及其所有关联的Child对象,并且对于每个Child对象,还要读取它引用的ReferencedByChild对象。

The whole code that reproduces my problem is below. 重现我的问题的整个代码如下。 What I am not 100% sure about is the @Relation on the OwnerWithEverything POJO. 什么我不是100%肯定是@RelationOwnerWithEverything POJO。 See below please. 请参阅下面。

@Database(
    entities = [
        Owner::class,
        Child::class,
        ReferencedByChild::class
    ],
    version = 1
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun appDao(): AppDao
}

@Dao
abstract class AppDao {
    @Insert
    abstract fun insertOwner(owner: Owner): Long

    @Insert
    abstract fun insertChild(child: Child): Long

    @Insert
    abstract fun insertReferencedByChild(referencedByChild: ReferencedByChild): Long

    @Query("SELECT * FROM Child INNER JOIN ReferencedByChild ON Child.referencedByChildId = ReferencedByChild.refByChildId ORDER BY Child.childText")
    abstract fun findAllChildrenWithReferencedClasses(): List<ChildWithReferenced>

    // Commenting this query out makes the build pass, so something here is incorrect.
    @Query("SELECT * FROM Owner")
    abstract fun findOwnersWithEverything(): List<OwnerWithEverything>
}

// ENTITIES
@Entity
data class Owner(
    @PrimaryKey(autoGenerate = true)
    val ownerId: Long,
    val ownerText: String
)

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Owner::class,
            parentColumns = arrayOf("ownerId"),
            childColumns = arrayOf("referencedOwnerId"),
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = ReferencedByChild::class,
            parentColumns = arrayOf("refByChildId"),
            childColumns = arrayOf("referencedByChildId"),
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class Child(
    @PrimaryKey(autoGenerate = true)
    val childId: Long,
    val childText: String,
    val referencedOwnerId: Long,
    val referencedByChildId: Long
)

@Entity
data class ReferencedByChild(
    @PrimaryKey(autoGenerate = true)
    val refByChildId: Long,
    val refText: String
)

// POJOS

// The Child has exactly one ReferencedByChild reference. This POJO joins those two
class ChildWithReferenced(
    @Embedded
    var child: Child,

    @Embedded
    var referencedByChild: ReferencedByChild
)

class OwnerWithEverything {
    @Embedded
    var owner: Owner? = null

    @Relation(
        parentColumn = "ownerId",
        entityColumn = "referencedOwnerId",
        entity = Child::class  // which entity should be defined here?
    )
    var childrenWithReferenced: List<ChildWithReferenced>? = null
}

Building this code results in this error message: 构建此代码将导致以下错误消息:

error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: refByChildId)

I think that the Owner query is badly constructed, but I am not entirely sure. 我认为Owner查询的构造错误,但是我不确定。 If that is the problem, what is the correct way to construct the query? 如果那是问题,那么构造查询的正确方法是什么?




This is the original question I have a nested POJO structure that should represent a single Game having multiple Rounds and each Round has a single Topic associated with it: 这是最初的问题,我有一个嵌套的POJO结构,该结构应表示一个具有多个Rounds Game ,每个Round都有一个与之关联的Topic

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
)

The embedded annotation on Topic specifies a prefix because there are clashing id properties. Topic上嵌入的注释指定了前缀,因为存在冲突的id属性。

The Room Query that can fetch those classes is: 可以获取这些类的Room Query是:

@Query("SELECT Topic.id as topic_id, Topic.name as topic_name, (...), RoundRoom.* FROM RoundRoom INNER JOIN Topic ON RoundRoom.topicId = Topic.id")
    abstract fun findRoundsWithTopics(): List<RoundWithTopic>

However, building the project gives me Room errors: 但是,构建项目给了我房间错误:

There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: topic_id)

Even though when I induce a warning about which fields are actually present, this is what Room tells me: 即使当我警告有关实际存在哪些字段的警告时,这也是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.

The topic_id column is there in the query result! 查询结果中有topic_id列! Why am I getting this error? 为什么会出现此错误?


For completeness, this is the entities: 为了完整起见,这是实体:

@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?
)

After some research, specifically looking at this link : How can I represent a many to many relation with Android Room? 经过研究,特别是看一下此链接: 我如何代表与Android Room的多对多关系? the only answers we found would be to 我们发现的唯一答案是

  1. either create a hand-made sql query to handle this type of situation where you have a many-to-many relationship 要么创建一个手工的sql查询来处理这种类型的情况,即您具有多对多关系

    OR 要么

  2. to alternatively have an additional joining entity which gets updated as the rest of the objects are updated. 或者具有一个附加的连接实体,该实体随其他对象的更新而更新。 With this approach, you can get the ID's and then create additional queries as needed 使用这种方法,您可以获取ID,然后根据需要创建其他查询

It seems embedded fields and type converter is not properly used on observing the question. 看来嵌入式字段和类型转换器未正确地用于观察问题。 I don't want to go in detail in the solution of the question since it is trying to use complex relations and I cannot test it replicating in my machine. 我不想在该问题的解决方案中进行详细介绍,因为它试图使用复杂的关系并且无法在我的机器中测试它的复制。

But I want to provide insight on using Embedded fields and TypeConverter . 但是我想提供有关使用Embedded字段和TypeConverter的见解。

Let's take an example from the question above: Game table has fields id, secondsPerTurn, maxSecondsPerTurn, bonusSecondsPerAnswer . 让我们以上面的问题为例: 游戏表具有字段id, secondsPerTurn, maxSecondsPerTurn, bonusSecondsPerAnswer

It is okay to create entity like below. 可以创建如下所示的实体。

@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
)

Here in the SQLite table, data is actually stored in four different columns but Room does CRUD operation based on data class structure giving higher feasibilty to the developers. 在SQLite表中,数据实际上存储在四个不同的列中,但是Room根据数据类结构执行CRUD操作,从而为开发人员提供了更高的便利性。

TypeConverter 类型转换器

Type converter will be helpful if we want to store non primitive data types or same type of data which will not be covered by @Embedded . 如果我们要存储非原始数据类型或@Embedded不会涵盖的相同类型的数据,类型转换器将非常有用。

For example, a football game can be held in two places: home and away. 例如,一场足球比赛可以在两个地方举行:主场和客场。 Home and Away can have the same field names like placeName, latitude, longitude . Home和Away可以具有相同的字段名称,例如placeName, latitude, longitude In this case, we can create data class and type converters like below: 在这种情况下,我们可以创建数据类和类型转换器,如下所示:

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)
    }
}

While using type converter, converter class should be defined in database class as below: 使用类型转换器时,应在数据库类中定义转换器类,如下所示:

@Database(
    entities = [Game::class /* ,more classes here*/],
    version = 1
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {

    abstract fun gameDao(): GameDao

    //.....
}

I hope this will help to deal with Embedded and TypeConverter in Room . 我希望这将有助于处理Room中的 EmbeddedTypeConverter

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Android Room Relations get limited fields from embedded model - Android Room Relations get limited fields from embedded model POJO结果查询室android中的歧义列 - Ambigous column in POJO result query room android Android 房间和 inheritance。 错误:多个字段具有相同的columnName - Android Room and inheritance. error: Multiple fields have the same columnName 如何将来自单个表中多个pojo类的特定值持久保存到Android的Room Database中? - How to persist particular values taken from multiple pojo classes in a single table into Room Database in Android? 如何在 android 中的房间 db 中获取映射到 pojo 的列信息 - how to get column info mapped to pojo in room db in android 在列表视图中显示POJO对象的多个字段 - Display multiple fields from POJO object in listview Android Room库 - 选择嵌入实体的所有字段 - Android Room library - select all fields for embedded entities 对于在库模块中定义的POJO的@NonNull带注释的构造函数参数,Android Room @Embedded批注编译失败 - Android Room @Embedded annotation compilation fails for @NonNull annotated constructor parameters of a POJO defined in a library module Android Room多个字段具有相同的columnName - Android Room Multiple fields have the same columnName 在会议室中访问和存储多个POJO类 - Accessing and Storing multiple POJO classes in Room
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM