您的问题似乎是 @Insert(onConflict = OnConflictStrategy.REPLACE) 正在插入而不是替换。
OnConflict 策略只有在存在冲突时才会被执行。这通常是违反了UNIQUE 约束 (有关其他违规情况,请参阅下面的链接)。
根据您对问题的解释,没有违规行为,因此没有替换行,而是插入了行。
简而言之,您需要一个唯一索引来涵盖您的场景,以确保发生违规。
如果没有 Article 类,就不可能说出需要进行哪些实际更改。但是,以下示例显示了 2 个类(实体)以及如何将唯一索引添加到列/属性 articleName。
所以首先猜测你的文章类可能是什么:-
@Entity
data class Article(
@PrimaryKey
var articelId: Long? = null,
var articleName: String,
var otherData: String
)
这里是 articleName 属性/列上具有唯一索引的等价物:-
@Entity(indices = [Index(value = ["articleName"],unique = true)])
data class ArticleV2(
@PrimaryKey
var articleId: Long? = null,
var articleName: String,
var otherData: String
)
示例/演示
作为两者工作方式不同的示例,后者根据您的期望,考虑以下代码,利用上述代码(为了简洁/方便在主线程上运行):-
两个表/实体/类的@Dao ArticleDao:-
@Dao
interface ArticleDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun upsert(article: Article): Long
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun upsert(articleV2: ArticleV2)
@Query("SELECT count(*) FROM article")
fun getArticleCount(): Long
@Query("SELECT count(*) FROM articlev2")
fun getArticleV2Count(): Long
}
@Database ArticleDatabase 类:-
@Database(
entities = [Article::class, ArticleV2::class],
version = 1
)
abstract class ArticleDatabase : RoomDatabase() {
abstract fun getArticleDao(): ArticleDao
companion object {
@Volatile
private var instance: ArticleDatabase? = null
private val LOCK = Any()
operator fun invoke(context: Context) = instance ?: synchronized(LOCK) {
instance ?: createDatabase(context).also { instance = it }
}
private fun createDatabase(context: Context) =
Room.databaseBuilder(
context.applicationContext,
ArticleDatabase::class.java,
"article_db.db"
)
.allowMainThreadQueries()
.build()
}
}
最后一个活动,通过循环 5 次在每个表中插入一行,然后在循环之后写入每个表中的行数,从而复制您的 “每次我按下按钮”表到日志。
class MainActivity : AppCompatActivity() {
lateinit var db: ArticleDatabase
lateinit var dao: ArticleDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = ArticleDatabase.invoke(this)
dao = db.getArticleDao()
for(i in 1..5) {
dao.upsert(Article(null, "Article", "otherdata$i"))
dao.upsert(ArticleV2(null,"Article","otherdata$i"))
}
Log.d("ARTICLECOUNT","Number of Articles in article table = " + dao.getArticleCount() + "... in articleV2 table = " + dao.getArticleV2Count())
}
}
运行(第一次)时,日志中的输出包含:-
D/ARTICLECOUNT: Number of Articles in article table = 5... in articleV2 table = 1
所以在 article 表中插入了 5 行,但在 articleV2 表中只有 1 行(因为 articleName 使用的值相同)。
运行 Database Inspector 后显示:-
文章表
articleV2 表
- 可以看出,otherData 列已全部替换,是 otherdata5
- 如果 OnConflict 策略为 IGNORE,则 otherData 的值为 otherdata1
附加
关于现在添加的评论和文章类。
首先,谢谢楼主。我还添加了文章类。你的解释很好,但我不明白我需要改变什么。
您需要确定什么构成重复条目。
如果未更改的 URL 可能最有可能表示重复。
所以你可以使用:-
@Entity( tableName = "articles", indices = [Index(value = ["url"],unique = true)] ) //<<<<<<<<< ONLY CHANGE IS THE @Entity
data class Article(
@PrimaryKey(autoGenerate = true)
var Id: Int? = null,
val author: String?,
val content: String?,
val description: String?,
val publishedAt: String?,
val source: Source?,
val title: String?,
val url: String?,
val urlToImage: String?
) : Serializable
但是,您可以决定多个列应构成不插入的重复项。这是一个显示如何使用多列(复合索引)的变体。
@Entity( tableName = "articles", indices = [Index(value = ["author","content","description"],unique = true)] ) //<<<<<<<<< ONLY CHANGE IS THE @Entity
data class Article(
@PrimaryKey(autoGenerate = true)
var Id: Int? = null,
val author: String?,
val content: String?,
val description: String?,
val publishedAt: String?,
val source: Source?,
val title: String?,
val url: String?,
val urlToImage: String?
) : Serializable
- 在这种情况下,如果作者、内容和描述相同,则为重复。
注意在所有情况下允许所有空值(毕竟?)如果没有验证来阻止所有值为空(即数据可能是无用的)。
您可能还希望考虑在插入后清除输入(某些可能只是),从而降低重复尝试的可能性。