@Query说它没有这些字段,尽管我在查询本身->KotlinAndroid Room中有它们

41ik7eoe  于 2023-02-16  发布在  Kotlin
关注(0)|答案(3)|浏览(72)

我在DAO中得到了以下代码:

@Query("select Conversation.*, User.* from Conversation join User on Conversation.createdBy = User.userUuid where conversationUuid = :conversationUuid")
fun selectAllForOverview(conversationUuid: UUID): LiveData<List<ConversationSelectAllForOverview>>

这是ConversationSelectAllForOverview

data class ConversationSelectAllForOverview(
    @Embedded(prefix = "arg0")
    val arg0: DbConversation,
    @Embedded(prefix = "arg1")
    val arg1: DbUser
)

我读到我需要用prefix注解我的字段,以消除字段名称相同时的错误。我得到了这个错误,我不知道如何删除它。我100%确定所有列都可用。由于DbConversationDbUser是从数据库中生成的,如何解决这个问题?DbConversationDbUser共享一些列,请参见此处的DbConversation定义:https://gist.github.com/Jasperav/381243e7b3cf387bfc0e9f1343f9faebDbUser看起来是一样的。

error: The columns returned by the query does not have the fields
 [conversationUuid,createdBy,tsCreated,distanceMapped,showOnMap,showOnOverview,allowMessagesByInRangeRegularUsers,allowMessagesByOutOfRangeRegularUsers,stillReadableForOutOfRangeRegularUsers,freedomInReplies,title,subject,likes,latitude,longitude,hasPassword,isSubscribed,showOnMapScreen,isLiked,bypassChecks,isHidden,nsfw,currentDirectEvents,totalDirectEventsAfterLastJoin,subscriptions,userUuid,username,karma,tsCreated,allowsPrivateChats,allowsNsfw,thisUserBlockedCurrentUser,incomingFriendshipRequest,outstandingFriendshipRequest,friends,bio,appRoleMapped]
 in entity.ConversationSelectAllForOverview even though they are
 annotated as non-null or primitive. Columns returned by the query:
 [conversationUuid,createdBy,tsCreated,distanceMapped,showOnMap,showOnOverview,allowMessagesByInRangeRegularUsers,allowMessagesByOutOfRangeRegularUsers,stillReadableForOutOfRangeRegularUsers,freedomInReplies,title,subject,likes,avatar,latitude,longitude,hasPassword,isSubscribed,showOnMapScreen,isLiked,bypassChecks,isHidden,conversationReportReasonMapped,nsfw,currentDirectEvents,totalDirectEventsAfterLastJoin,lastReadConversationEventPk,mostRecentConversationEventUuid,relevance,subscriptions,userUuid,username,karma,tsCreated,allowsPrivateChats,allowsNsfw,avatar,currentUserBlockedThisUserTsCreated,thisUserBlockedCurrentUser,searchScreenScore,recentSearchedTsCreated,userReportReasonMapped,incomingFriendshipRequest,outstandingFriendshipRequest,friends,bio,appRoleMapped]
     public abstract androidx.lifecycle.LiveData<java.util.List<entity.ConversationSelectAllForOverview>>
 selectAllForOverview(@org.jetbrains.annotations.NotNull()
z31licg0

z31licg01#

    • 问题所在**

@Embedded注解中的列加上前缀,同时输出列没有根据查询加上前缀。
例如,ConversationSelectAllForOverview类希望在查询的输出/结果中找到名为arg0conversationUuid的列,但查询只有列conversationUuid

    • 修正**

不使用select Conversation.*, User.* ....,而需要使用

select Conversation.conversationUuid AS arg0conversationUuid, Conversation.createdBy AS arg0createdBy ....
      • AS为输出 * 对话Uuid * 列提供 * arg0对话Uuid * 的别名**等。

例如,对于两个表中的每一列,您必须使用其前缀来别名实际列。
例如,使用(仅部分调整):-

@Query("select " +
        "Conversation.conversationUuid AS arg0conversationUuid" +
        ", Conversation.createdBy AS arg0createdBy" +
        ", Conversation.tsCreated AS arg0tsCreated" +
        ", Conversation.distanceMapped AS arg0distanceMapped" +
        /* .... */
        ", User.* from Conversation join User on Conversation.createdBy = User.userUuid where conversationUuid = :conversationUuid")
fun selectAllForOverview(conversationUuid: UUID): LiveData<List<ConversationSelectAllForOverview>>
  • 则消息是The columns returned by the query does not have the fields [showOnMap,showOnOverview ....
  • 即,conversationUuid、createdBy、tsCreated和distanceMapped现在不包括在字段-列不匹配列表中。
    • 替代修复**(未经测试且依赖于Room 2.5.0库)

另一种可能适用于Room 2.5.0(未测试)的解决方案是使用@Relation注解代替子对象(ren)的@Embedded注解。例如,除了以下内容之外,不进行任何其他更改:-

data class ConversationSelectAllForOverview(
    @Embedded/*(prefix = "arg0")*/
    val arg0: DbConversation,
    @Relation(
        entity = DbUser::class,
        parentColumn = "createdBy",
        entityColumn = "userUuid"
    )
    val arg1: DbUser
)

然后使用:-

@Transaction
@Query("SELECT * FROM Conversation WHERE conversationUUid=:conversationUuid")
fun selectAllForOverview(conversationUuid: UUID): LiveData<List<ConversationSelectAllForOverview>>

然后,它成功编译(再次不运行,见下文)。即,重复列没有问题(再次见下文)。
您还可以使用(注意函数名不同,以允许编译两者):-

@Transaction
@Query("select Conversation.*,User.* from Conversation join User on Conversation.createdBy = User.userUuid where conversationUuid = :conversationUuid")
fun selectAllForOverviewAlt(conversationUuid: UUID): LiveData<List<ConversationSelectAllForOverview>>
      • 但是****JOINed列是多余的,因为Room使用@Relationship参数,然后通过后续查询构建DbUser对象(因此使用@Transaction)。
        注意空间用于获取找到的最后一个值,对于类似命名的输出列,并将最后一个值应用于所有如此命名的字段。据报道,这已在2.5.0中得到修复。但尚未确认是否如此。因此,您可能会得到意想不到的结果,因此,如果您采用这种方法,则应确认值与预期值一致(即检查名称相似的列/字段的值)。
    • 附加演示**

以下是一个基于问题DbConversation类代码的工作演示。但是,为了简化问题,我们编写了其他代码,另外还注解掉了许多字段。LiveData已注解掉,.allowMainThreadQueries已简化演示。
Demo使用了两个修复程序,对于@Relation,使用了原始查询和建议的更合理的查询。
已使用调试断点来演示3个返回集。

    • 数据库代码**(演示所需的一切,加上一些不是由于注解掉)。应该注意的是,许多代码可能会有所不同,已经作出了简单的假设。

:-

@Entity(
    tableName = "Conversation",
    primaryKeys = ["conversationUuid"],
    indices = [/*Index(value = ["nsfw", "relevance"]), Index(value = ["isSubscribed"]),*/ Index(value = ["createdBy"])/*, Index(
        value = ["avatar"]
    ), Index(value = ["mostRecentConversationEventUuid"])*/],
    foreignKeys = [/*ForeignKey(
        entity = DbConversationEventMostRecent::class,
        childColumns = ["mostRecentConversationEventUuid"],
        parentColumns = ["conversationEventUuid"],
        onDelete = SET_NULL,
        onUpdate = CASCADE,
    ), ForeignKey(
        entity = DbMedia::class,
        childColumns = ["avatar"],
        parentColumns = ["mediaUuid"],
        onDelete = CASCADE,
        onUpdate = NO_ACTION,
    ), */ForeignKey(
        entity = DbUser::class,
        childColumns = ["createdBy"],
        parentColumns = ["userUuid"],
        onDelete = CASCADE,
        onUpdate = NO_ACTION,
    )]
)
data class DbConversation(
    @ColumnInfo(typeAffinity = ColumnInfo.TEXT)
    val conversationUuid: UUID,
    @ColumnInfo(typeAffinity = ColumnInfo.TEXT)
    val createdBy: UUID,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val tsCreated: Long,
    @ColumnInfo(typeAffinity = ColumnInfo.TEXT)
    val distanceMapped: ConversationDistance,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val showOnMap: Boolean,
    /*
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val showOnOverview: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val allowMessagesByInRangeRegularUsers: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val allowMessagesByOutOfRangeRegularUsers: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val stillReadableForOutOfRangeRegularUsers: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val freedomInReplies: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.TEXT)
    val title: String,
    @ColumnInfo(typeAffinity = ColumnInfo.TEXT)
    val subject: String,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val likes: Long,
    @ColumnInfo(typeAffinity = ColumnInfo.TEXT)
    val avatar: UUID?,
    @ColumnInfo(typeAffinity = ColumnInfo.REAL)
    val latitude: Double,
    @ColumnInfo(typeAffinity = ColumnInfo.REAL)
    val longitude: Double,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val hasPassword: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val isSubscribed: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val showOnMapScreen: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val isLiked: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val bypassChecks: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val isHidden: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.TEXT)
    val conversationReportReasonMapped: ConversationReportReason?,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val nsfw: Boolean,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val currentDirectEvents: Long,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val totalDirectEventsAfterLastJoin: Long,
    @ColumnInfo(typeAffinity = ColumnInfo.BLOB)
    val lastReadConversationEventPk: ConversationEventPk?,
    @ColumnInfo(typeAffinity = ColumnInfo.TEXT)

    val mostRecentConversationEventUuid: UUID?,
    @ColumnInfo(typeAffinity = ColumnInfo.REAL)
    val relevance: Double?,
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val subscriptions: Long
    */

)

@Dao
interface AllDAOs {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(dbUser: DbUser): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(dbConversation: DbConversation): Long

    @Query("select " +
            "Conversation.conversationUuid AS arg0conversationUuid" +
            ", Conversation.createdBy AS arg0createdBy" +
            ", Conversation.tsCreated AS arg0tsCreated" +
            ", Conversation.distanceMapped AS arg0distanceMapped" +
            ", Conversation.showOnMap AS arg0showOnMap" +
            /* .... */
            ",User.userUuid AS arg1userUuid" + /*?????? made up/incomplete/asssumed */
            ",User.userName AS arg1userName" +
            " from Conversation join User on Conversation.createdBy = User.userUuid where conversationUuid = :conversationUuid")
    fun selectAllForOverviewOld(conversationUuid: UUID): /*LiveData<*/List<OldConversationSelectAllForOverview>/*>*/

    @Query("SELECT * FROM Conversation WHERE conversationUuid=:conversationUuid")
    fun selectConversationByUuid(conversationUuid: UUID): List<DbConversation>

    @Query("SELECT * FROM Conversation WHERE conversationUuid=:conversationUuid")
    fun selectAllForOverview(conversationUuid: UUID): /*LiveData<*/List<ConversationSelectAllForOverview>/*>*/

    @Query("select Conversation.*,User.* from Conversation join User on Conversation.createdBy = User.userUuid where conversationUuid = :conversationUuid")
    fun selectAllForOverviewAlt(conversationUuid: UUID): /*LiveData<*/List<ConversationSelectAllForOverview>/*>*/
}

data class ConversationDistance(
    val blah: String
)
data class ConversationReportReason(
    val blah: String
)
data class ConversationEventPk(
    val blah: ByteArray
)

class RoomTypeConverters {
    @TypeConverter
    fun fromConversationDistanceToJSON(conversationDistance: ConversationDistance): String = Gson().toJson(conversationDistance)
    @TypeConverter
    fun toConversationDistanceFromJSON(json: String): ConversationDistance = Gson().fromJson(json,ConversationDistance::class.java)
    @TypeConverter
    fun fromConversationReportReasonToJSON(conversationReportReason: ConversationReportReason): String = Gson().toJson(conversationReportReason)
    @TypeConverter
    fun toConversationReportReasonFromJSON(json: String): ConversationReportReason = Gson().fromJson(json,ConversationReportReason::class.java)
    @TypeConverter
    fun fromConversationEventPkToByteArray(conversationEventPk: ConversationEventPk): ByteArray = ByteArray(100)
    @TypeConverter
    fun toConversationEventPkFromByteArray(byteArray: ByteArray): ConversationEventPk = ConversationEventPk(byteArray)

}

@Entity(tableName = "User")
data class DbUser(
    @PrimaryKey
    val userUuid: UUID,
    val userName: String
)
@Entity
data class DbMedia(
    @PrimaryKey
    val mediaUuid: UUID,
    val mediaName: String
)
@Entity
data class DbConversationEventMostRecent(
    @PrimaryKey
    val conversationEventUuid: UUID
)

data class ConversationSelectAllForOverview(
    @Embedded/*(prefix = "arg0")*/
    val arg0: DbConversation,
    @Relation(
        entity = DbUser::class,
        parentColumn = "createdBy",
        entityColumn = "userUuid"
    )
    val arg1: DbUser
)

data class OldConversationSelectAllForOverview(
    @Embedded(prefix = "arg0")
    val arg0: DbConversation,
    @Embedded(prefix = "arg1")
    val arg1: DbUser
)

@TypeConverters(value = [RoomTypeConverters::class])
@Database(entities = [DbMedia::class,DbUser::class,DbConversationEventMostRecent::class,DbConversation::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getAllDAOs(): AllDAOs
    companion object {
        private var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if (instance==null) {
                instance=Room.databaseBuilder(context,TheDatabase::class.java,"the_database")
                    .allowMainThreadQueries() /* For brevity of demo */
                    .build()
            }
            return instance as TheDatabase
        }
    }
}

活动代码:-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: AllDAOs
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = TheDatabase.getInstance(this)
        dao = db.getAllDAOs()

        val u1uuid = UUID(10L,10L)
        val u2uuid = UUID(11L,12L)
        val c1uuid = UUID(20L,20L)
        val c2uuid = UUID(21L,21L)
        val c3uuid = UUID(22L,10L)
        val c4uuid = UUID(15L,10L)

        dao.insert(DbUser(u1uuid,"Fred"))
        dao.insert(DbUser(u2uuid,"Mary"))
        dao.insert(DbConversation(c1uuid,u1uuid,0, ConversationDistance("blah blah blah"),true))
        dao.insert(DbConversation(c2uuid,u2uuid,0,ConversationDistance("uhm uhm uhm"),true))
        dao.insert(DbConversation(c3uuid,u1uuid,0,ConversationDistance("meh meh meh"),true))
        dao.insert(DbConversation(c4uuid,u1uuid,0,ConversationDistance("good good good"),true))

        val c1 = dao.selectConversationByUuid(c1uuid)
        val c2 = dao.selectConversationByUuid(c2uuid)
        val c3 = dao.selectConversationByUuid(c3uuid)
        val c4 = dao.selectConversationByUuid(c4uuid)

        val t1c1 = dao.selectAllForOverviewOld(c1uuid)
        val t1c2 = dao.selectAllForOverview(c1uuid)
        val t1c3 = dao.selectAllForOverviewAlt(c1uuid)

        val t2c1 = dao.selectAllForOverviewOld(c2uuid)
        val t2c2 = dao.selectAllForOverview(c2uuid)
        val t2c3 = dao.selectAllForOverviewAlt(c2uuid)

        if (t1c1==null) t1c1==null /*<<<<<<<<<< BREAKPOINT HERE >>>>>>>>>>*/
    }
}

当从全新安装运行时,则调试窗口(扩展了t1??和t2??):-

可以看出,所有3个查询都产生相同的结果。因此,最简单的解决方案是

  • 确保您使用的是Room 2.5.0库,并且
  • 使用@Relation而不是@Embedded,并使用更简洁的查询来提取相关的DbConversation。
owfi6suc

owfi6suc2#

查看类DbConversation和DbUser,确保这些类是@Embeddable。如果不是,在类上添加注解。另一个解决方案是再次查看错误。它说查询返回了一堆DTO(ConversationSelectAllForOverview)不包含的列;DTO的字段是对象而不是列字段。您可以创建一个接口来接收db返回的所有列:

interface nameInterface(
    val conversationUuid: Any,
    val createdBy: Any,
    val tsCreated: Any,
    val distanceMapped: Any,
    val showOnMap: Any,
    val showOnOverview: Any,
    val allowMessagesByInRangeRegularUsers: Any,
    val allowMessagesByOutOfRangeRegularUsers: Any,
    val stillReadableForOutOfRangeRegularUsers: Any,
    val freedomInReplies: Any,
    val title: Any,
    val subject: Any,
    val likes: Any,
    val latitude: Any,
    val longitude: Any,
    val hasPassword: Any,
    val isSubscribed: Any,
    val showOnMapScreen: Any,
    val isLiked: Any,
    val bypassChecks: Any,
    val isHidden: Any,
    val nsfw: Any,
    val currentDirectEvents: Any,
    val totalDirectEventsAfterLastJoin: Any,
    val subscriptions: Any,
    val userUuid: Any,
    val username: Any,
    val karma: Any,
    val tsCreated: Any,
    val allowsPrivateChats: Any,
    val allowsNsfw: Any,
    val thisUserBlockedCurrentUser: Any,
    val incomingFriendshipRequest: Any,
    val outstandingFriendshipRequest: Any,
    val friends: Any,
    val bio: Any,
    val appRoleMapped: Any)
vql8enpb

vql8enpb3#

错误消息告诉您ConversationSelectAllForOverview类中的字段与SQL查询返回的列不匹配。具体来说,它列出了查询返回的列和类中的字段,并显示存在一些不匹配。

DbConversationDbUser之间似乎有一些重复的列名,而您正在使用**@Embedded注解将它们都包含在ConversationSelectAllForOverview**类中。但是,您需要为每个@Embedded注解使用不同的前缀以消除字段的歧义。

例如,您可以对DbConversation字段使用前缀=“conversation_",对DbUser字段使用前缀=“user_"

data class ConversationSelectAllForOverview(
@Embedded(prefix = "conversation_")
val conversation: DbConversation,
@Embedded(prefix = "user_")
val user: DbUser )

然后,在SQL查询中,可以使用以下前缀引用列:

select conversation.*, user.*
from Conversation as conversation
join User as user on conversation.createdBy = user.userUuid
where conversationUuid = :conversationUuid

请注意,您不需要在列名本身中包含前缀,因为Room会根据**@Embedded**注解自动应用这些前缀。

相关问题