Kotlin 使用Room持久性库,我可以自己设置主键并管理它的“唯一性”吗?

Kotlin 使用Room持久性库,我可以自己设置主键并管理它的“唯一性”吗?,kotlin,primary-key,android-room,Kotlin,Primary Key,Android Room,我正在使用房间图书馆。我的一个实体编码如下: @Entity data class Authentication ( @PrimaryKey @ColumnInfo(name = "system_id") val systemID: String = "demo", @ColumnInfo(name = "password") val password: String? = "demo", @ColumnInfo(name = "server_ad

我正在使用房间图书馆。我的一个实体编码如下:

@Entity
data class Authentication (
    @PrimaryKey
    @ColumnInfo(name = "system_id")
    val systemID: String = "demo",
    @ColumnInfo(name = "password")
    val password: String? = "demo",
    @ColumnInfo(name = "server_address")
    val serverAddress: String? = "https://www.someSite.com/login/"
)
将systemID设置为主键。这是我的刀:

我正在管理服务器上的systemId。我知道它们都是独一无二的。然而,当我尝试插入一个对象时,我的代码抛出一个异常,指出我的主键失败了,这是唯一的约束。当我在上面的代码中插入onConflict=OnConflictStrategy.REPLACE时,一切正常,但在我把它放在那里之前,我的应用程序崩溃了

我查看了文档,没有找到我问题的答案。我想知道为什么即使我知道我没有尝试插入另一个具有相同systemID的对象,也会抛出此错误。房间不信任我吗?是因为我不允许Room自动生成主键,因此Room和编译器不能相信我不会复制主键吗?还是我错过了什么

提前谢谢

编辑

也许我应该在我的问题中加入这一点。这就是让我困惑的代码

fun checkForAuthenticationInDatabase() {
        launch(IO){
            var auth = AuthDatabase(getApplication()).authDao().getAuth()
            if (auth == null){
                Log.d("TAG", "auth was null")
                auth = Authentication()
                AuthDatabase(getApplication()).authDao().insertAuth(auth)
                Log.d("TAG", auth.toString())
            }
            withContext(Main){
                authentication.value = auth
            }
        }
    }
如你所见,我首先检查一个物体的存在。如果它不存在并且返回null,那么我插入一个。如果它真的存在,我就抓住它。在发布这个问题之前,我没有注意到的是,最初我使用int作为主键,从1开始,没有自动生成。此数据库中应该只有一条rowrecord。然后我将其更改为使用systemID作为主键,而不是int,但我忘了更改查询:

@Query("SELECT * FROM authentication WHERE system_id = 1")
    suspend fun getAuth(): Authentication?
我意识到我的错误,想把这篇文章发给可能犯同样错误的人

即使我知道我没有尝试插入另一个具有相同systemID的对象

根据您对错误的描述以及使用REPLACE修复问题的描述,数据库中有一行与您尝试插入的系统id相同

请注意,给出的描述与您的描述略有不同。SQLite(Room基本上是一个包装器)没有存储对象的概念,而是按行存储数据,每行包含相同数量的元素,尽管有些元素可能为空

应用程序中的对象之间的区别,即使您将数据库中的一行等同于它可能表示并将在房间中显示的对象,也是因为在数据库中,当应用程序重新启动时,数据被持久化并保留在数据库中。也许正是这种坚持让你困惑

如果您更改/使用

@Query("SELECT * FROM authentication WHERE system_id = :system_id")
suspend fun getAuth(system_id: String): Authentication?
从身份验证中选择@QuerySELECT*,其中系统\u id=1 挂起getAuth:身份验证?是非常无用的,因为它只允许您从数据库中提取1个身份验证,一个可能不存在的身份验证。 在插入之前使用这个,使用系统id,你正在插入的这个很可能会符合这种情况。也就是说,它将找到数据并返回一个有效的非空身份验证对象

房间不信任我吗? 在某种程度上是的,在某种程度上不是

是因为我不允许Room自动生成主键,因此Room和编译器不能相信我不会复制主键吗

不需要。您的代码工作正常,并且在给定时允许唯一性

还是我错过了什么? 如上所述,我认为情况就是这样。同样,对于system_id的唯一值,代码工作正常。这确实假设没有其他实体

考虑以下哪一条-

从身份验证添加@QuerySELECT* 有趣的getAllAuth:列表到AuthDao 为方便起见,删除挂起以允许从主线程运行 使用您的代码:-

    val authDatabase = Room.databaseBuilder(this,AuthDatabase::class.java,"authdb")
        .allowMainThreadQueries()
        .build()
    var a1 = Authentication("system_id1","password","www.server.etc")
    var a2 = Authentication("system_id2","password","xxx.server.etc")
    Log.d("AUTHINSERT","Insert of Authentication returned " + authDatabase.authDao().insertAuth(a1))
    Log.d("AUTHINSERT","Insert of Authentication returned " + authDatabase.authDao().insertAuth(a2))
    Log.d("AUTHINSERT","Insert of Authentication returned " + authDatabase.authDao().insertAuth(a1))
    var authentications = authDatabase.authDao().getAllAuth()
    for (authentication in authentications) {
        Log.d("AUTHINFO",authentication.systemID)
    }
第一次运行此操作将导致日志包含:-

2020-01-09 16:39:58.351 D/AUTHINSERT: Insert of Authentication returned 1
2020-01-09 16:39:58.352 D/AUTHINSERT: Insert of Authentication returned 2
2020-01-09 16:39:58.354 D/AUTHINSERT: Insert of Authentication returned 3
2020-01-09 16:39:58.358 D/AUTHINFO: system_id2
2020-01-09 16:39:58.358 D/AUTHINFO: system_id1
也就是说,插入了三行,rowid为1-3

rowid是一个通常隐藏的行,它具有由SQLite SQLite提供的唯一的64位带符号整数,除非rowid有别名。 但是,仅输出了2个身份验证对象。这是因为rowid为1的行已被删除,而rowid为3的行已被添加。这就是REPLACE所做的

如果再次运行上述代码,则结果为:-

2020-01-09 16:44:25.455 D/AUTHINSERT: Insert of Authentication returned 4
2020-01-09 16:44:25.456 D/AUTHINSERT: Insert of Authentication returned 5
2020-01-09 16:44:25.458 D/AUTHINSERT: Insert of Authentication returned 6
2020-01-09 16:44:25.462 D/AUTHINFO: system_id2
2020-01-09 16:44:25.462 D/AUTHINFO: system_id1

也就是说,数据库中的数据已被保留,因为对象中的数据(尽管它们不是垃圾收集和删除的对象)是相同的。这两行已被删除,插入的新行rowid的5和6将在数据库中

谢谢你,米凯特。读了你的回复后,我意识到我的错误。请随意查看我的编辑以查看。我应该在我原来的问题上多加一段代码。无论如何,干杯。功劳
2020-01-09 16:44:25.455 D/AUTHINSERT: Insert of Authentication returned 4
2020-01-09 16:44:25.456 D/AUTHINSERT: Insert of Authentication returned 5
2020-01-09 16:44:25.458 D/AUTHINSERT: Insert of Authentication returned 6
2020-01-09 16:44:25.462 D/AUTHINFO: system_id2
2020-01-09 16:44:25.462 D/AUTHINFO: system_id1