我正在使用Room库。我的实体之一的编码如下:
@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
设置为主键。这是我的Dao
:@Dao
interface AuthDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAuth(auth: Authentication): Long
@Query("SELECT * FROM authentication WHERE system_id = 1")
suspend fun getAuth(): Authentication?
@Update
suspend fun updateAuth(auth: Authentication): Int
@Delete
suspend fun deleteAuth(auth: Authentication)
}
我正在管理服务器上的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
}
}
}
如您所见,我首先检查对象的存在。如果它不存在并且返回空值,那么我插入一个。如果确实存在,我就捕获它。在发布此问题之前,我没有意识到的是,最初我是将int用作主键(从1开始且没有自动生成)。该数据库中应该只有一行(记录)。然后我将其更改为使用systemID作为主键而不是int,但是我忘记更改查询:
@Query("SELECT * FROM authentication WHERE system_id = 1")
suspend fun getAuth(): Authentication?
我意识到自己的错误,并希望将其发布给可能犯同样错误的其他人
最佳答案
even when I KNOW I haven't tried to insert another object with the same systemID
从对错误的描述以及使用REPLACE可以解决该问题之后,数据库中就有一行与您要插入的system_id相同。
请注意,给出的描述与您的描述有些不同。对于Room基本上是包装器的SQLite,它没有存储对象的概念,而是将数据存储在行中,每行包含相同数量的元素(尽管有些元素可能为null)。
应用程序中的对象之间的差异以及即使您在数据库中将一行等同于一个对象(它可能表示并且将在Room中)之间的区别在于,数据库在数据库中的数据将保留并在重新启动应用程序时保留在数据库中。也许正是这种坚持让您感到困惑。
如果您更改/使用
@Query("SELECT * FROM authentication WHERE system_id = :system_id")
suspend fun getAuth(system_id: String): Authentication?
具有
@Query("SELECT * FROM authentication WHERE system_id = 1")
suspend fun getAuth(): Authentication?
的并在插入之前使用system_id来使用它,这很可能会使情况混乱。那就是它将找到数据并返回有效(非null)的Authentication对象。
Does Room not trust me? To some extent yes, to some extent no.
Is it because I didn't allow Room to autogenerate the primary key and thus Room and the compiler can't trust that I won't duplicate primary keys?
不会。您的代码可以正常工作,并且在给出代码时允许唯一性。
Or am I missing something else? As explained above, I believe that this is the case. Again with unique values for system_id the code works fine (this does assume that there are no other entities).
考虑以下哪个栏-
@Query("SELECT * FROM authentication")
fun getAllAuth(): List<Authentication>
添加到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为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将在数据库中)。
关于kotlin - 使用Room Persistence库,我可以设置自己的主键并自己管理它的 “uniqueness”吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59655934/