luc*_*ter 6 java sqlite android kotlin android-room
我正在实现以下模式来计算用户的按钮点击次数。例如,如果用户单击苹果按钮,我想将类型存储为主键,这将是一个唯一的条目,后跟用户点击苹果按钮的次数的计数,对于每个按钮,我也会保存与这些点击相关的时间。因此,我正在尝试学习房间数据库,并想了解如何使用新的关联计数和时间戳来更新独特的水果类型行。我是否必须查询表getByType(type)以获取现有的数据条目,然后每次都使用新的计数和时间戳值更新它,或者是否有更好的方法使用 room api 来执行此操作
@Entity
data class ClickEntity(
@PrimaryKey
val type: String,
val clickCount: Int? = null,
val clickTime: Long? = null
)
@Dao
internal interface ClickEntityDao {
@Query("SELECT * FROM ClickEntity ORDER BY type")
fun getAll(): List<ClickEntity>
@Query("SELECT * FROM ClickEntity WHERE type=:type")
fun getByType(entity: ClickEntity): ClickEntity
// QUESTION: type, count, time -> how do I increment counter and update time while updating an existing entry
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun update(entity: ClickEntity)
@Query("DELETE FROM ClickEntity")
fun clear()
}
Run Code Online (Sandbox Code Playgroud)
我相信以下内容可以满足您的要求:-
@Query("UPDATE ClickEntity SET clickCount = clickCount + 1, clickTime = :clickTime WHERE type=:type")
fun incrementClick(type: String, clickTime: Long)
Run Code Online (Sandbox Code Playgroud)
例如以下内容:-
dao.insert(ClickEntity("Apple",0,0))
dao.incrementClick("Apple",System.currentTimeMillis());
Run Code Online (Sandbox Code Playgroud)
结果是 :-
即,在更新计数增加并且当前时间已相应设置后,插入计数和时间为 0 时。因此,无需获取数据然后应用(只要您知道它相当于按钮时应该使用的类型)。
补充评论
在更新和递增之前我仍然需要插入一次!是否有一个较短的版本可以同时插入或更新。
是的,也许吧。但是,如果您有一个按钮,那么我建议您在知道应该有该按钮时插入。
如果应用程序针对具有 SQLite 版本 3.24.0(Android API 30+ 版本 11+)的 Android 版本,那么您可以使用 UPSERT (带有 ON CONFLICT() DO UPDATE 子句的 INSERT)。
例如
@Query("INSERT INTO clickEntity VALUES(:type,0,:clickTime) ON CONFLICT(type) DO UPDATE SET clickCount = clickCount +1, clickTime = :clickTime ")
fun insertOrUpdate(type: String, clickTime: Long)
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用 INSERT OR REPLACE,这只需使用:-
@Insert(onConflictStategy = OnConflictStrategy.REPLACE)
fun insert(ClickEntity("the_type",the_new_count,the_current_timestamp)
Run Code Online (Sandbox Code Playgroud)
但你必须检索计数。
然而,更复杂的解决方案可能是使用子查询获取类型的计数+1。这很复杂,因为第一次插入时没有计数。这可以使用 SQLitecoalesce函数来解决。这将需要@Query,因为@Insert需要一个Entity对象。因此,插入或替换(更新但实际上删除并插入以执行更新)的解决方案可能是:-
@Query("INSERT OR REPLACE INTO clickentity VALUES(:type,coalesce((SELECT clickCount FROM clickEntity WHERE type = :type),-1) + 1,strftime('%s','now') * 1000)")
fun insertOrReplace(type: String)
Run Code Online (Sandbox Code Playgroud)
所以:-
strftime,并将其乘以 1000,使其与 Android 时间戳一致(增加毫秒)。示例(INSERT 或 REPLACE 选项):-
继上述代码之后,除了上述 Dao 的更改/添加之外,还添加了以下内容(不是 UPSERT):-
// Adds new
dao.insert(ClickEntity("Grape",0, System.currentTimeMillis()))
// Updates (cheated with clickCount knowing it would be 1)
dao.insert(ClickEntity("Grape",1,System.currentTimeMillis()))
// Adds new
dao.insertOrReplace("Apricot")
// Updates
dao.insertOrReplace("Apricot")
Run Code Online (Sandbox Code Playgroud)
数据库看起来像(所有 Android Studio Show):-