[英]“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%肯定是
@Relation
上OwnerWithEverything
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
我们发现的唯一答案是
either create a hand-made sql query to handle this type of situation where you have a many-to-many relationship 要么创建一个手工的sql查询来处理这种类型的情况,即您具有多对多关系
OR 要么
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中的 Embedded和TypeConverter 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.