Room DB - 用 autoIncrement 替换策略
Room DB - Replace strategy with autoIncrement
我在一个 android 应用程序中工作,该应用程序将来自远程服务器的数据存储在房间数据库中,然后从 Room DB 本身显示数据。
我的问题是我使用 autoIncrement
作为主键字段,我想使用 OnConflictStrategy.REPLACE
将旧数据替换为来自远程服务器的新数据,我已经编写了代码。
但是如何替换数据因为总是autoIncrement
id 会不同并且每次都会插入数据而不是替换。
But how data will get replaced because always autoIncrement id will be different and data will inserted everytime instead of replace. Noting that the id will then be changed.
要使替换策略起作用,您必须确定与数据关联的行。您将根据唯一标识该行的 id 以外的数据执行此操作。
但是,使用替换是过时且低效的,因为知道如何唯一标识行意味着您可以使用更新(不是 @Update
,而是 @Query("UPDATE thetable SET the_first_changed_column = the_new value, the_next_changed_column_if one = the the_new_value /* more comma separated columns */ WHERE a_where_clause_that_uniquely_identifies_row")
)。
@Update 基于传递对象的主键值。
REPLACE 是低效的,因为它通过删除现有行来工作,然后需要相应地更改索引,然后插入一个新行 (如下所示可能有如果已指定 id,则相同的 id(请参阅有关第三个块的注释)),这需要相应地更改索引。
因为有这个“其他数据”应该唯一标识一行,所以使用 @Entity
注释的 indicies =
参数在这些列上建立索引会很明显相应 Index 的 unique
参数为真。
- 举个例子,其中 lastTime 列和 lastMessage 列唯一标识一行,那么你可以
indices = [Index(value = ["lastTime","lastMessage"], unique = true, name ="optional_name_of_index")]
- 名称是可选的
工作Example/Demo
下面是一个演示上述大部分内容的示例。
它基于一个带注释的@Entity class 示例 :-
@Entity(
indices = [
Index( value = ["serverName","itemCode"], unique = true)
]
)
data class Example(
@PrimaryKey
var id: Long? = null,
val serverName: String, /* UID Part 1 */
val itemCode: String, /* UID part 2 */
var otherData1: String,
var otherData2: String
)
var 用于 id、otherData1 和 otherData2,因此可以更改值。
可以看出,已经为应该唯一的两列添加了索引。
请注意,不需要 autogenerate = true,实际上这是低效的,因为它引入了 SQLite AUTOINCREMENT 关键字。但是,您确实需要将 id 值设置为 NULL 而不是 0。
- link regrading AUTOINCREMENT 的第一段说 “AUTOINCREMENT 关键字强加了额外的 CPU、内存、磁盘 space 和磁盘I/O 开销,如果不是严格需要,应该避免。通常不需要。"
@Dao 注释 class AllDAO 用于插入,建议的@Query 用于根据唯一数据进行更新,@Query 用于提取数据:-
@Dao
interface AllDAO {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(example: Example): Long
@Query("UPDATE example SET otherData1=:newOtherData1, otherData2=:newOtherData2 WHERE serverName=:serverName AND itemCode=:itemCode")
fun update(serverName: String, itemCode: String, newOtherData1: String, newOtherData2: String): Int
@Query("SELECT * FROM example")
fun getAllExamples(): List<Example>
}
- 注意 return 值
- 插入的 long 将是生成的 id
- update出来的Int会是更新的行数,虽然这个没有取到,应该是1
@Database 注释 class 以便实际演示 运行 可以是 运行 :-
@Database(entities = [Example::class], version = 1, exportSchema = false)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDAO(): AllDAO
companion object {
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
最后 activity 代码:-
- 插入一些数据
- 使用 REPLACE 更新数据
- 使用建议的 UPDATE
更新数据
- 从数据库中提取数据,在某些情况下从当前的示例对象中提取数据,以了解正在发生的事情
:-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDAO()
var ex1 = Example(serverName = "S1", itemCode = "I1", otherData1 = "AA", otherData2 = "BB")
var ex2 = Example(serverName = "S1", itemCode = "I2", otherData1 = "CC", otherData2 = "DD")
var ex3 = Example(serverName = "S1", itemCode = "I3", otherData1 = "EE", otherData2 = "FF")
ex1.id = dao.insert(ex1)
logAnExampleObject(ex1,"STAGE_1A") /* write the ex1 object to the log NOT from the db */
ex2.id = dao.insert(ex2)
logAnExampleObject(ex2,"STAGE_1B")
ex3.id = dao.insert(ex3)
logAnExampleObject(ex3,"STAGE_1C")
logAllExamples("_STAGE1Z") /* write all examples as extracted from the db */
ex1.otherData1 = "AAAA"
ex1.otherData2 = "BBBB"
dao.insert(ex1) /* NOTE ID AS PER INSERT due to updating value when inserted*/
logAllExamples("_STAGE2A")
ex1.id = null
ex1.otherData1 = "AAAAAA"
ex1.otherData2 = "BBBBBB"
dao.insert(ex1) /* NOTE ID set to NULL so as to use generated id */
logAllExamples("_STAGE2B")
dao.update("S1","I2","CCCCCC","DDDDDD")
logAllExamples("_STAGE3")
}
fun logAllExamples(prefix: String) {
for (ex in dao.getAllExamples()) {
logAnExampleObject(ex,prefix)
}
}
private fun logAnExampleObject(ex: Example, prefix: String) {
Log.d("USERINFO${prefix}","ID is ${ex.id} Server is ${ex.serverName} ItemCode is ${ex.itemCode} D1 is ${ex.otherData1} D2 is ${ex.otherData2}")
}
}
结果
第一次运行时,日志包括:-
2022-04-30 15:32:30.017 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFOSTAGE_1A: ID is 1 Server is S1 ItemCode is I1 D1 is AA D2 is BB
2022-04-30 15:32:30.019 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFOSTAGE_1B: ID is 2 Server is S1 ItemCode is I2 D1 is CC D2 is DD
2022-04-30 15:32:30.024 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFOSTAGE_1C: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.033 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE1Z: ID is 1 Server is S1 ItemCode is I1 D1 is AA D2 is BB
2022-04-30 15:32:30.033 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE1Z: ID is 2 Server is S1 ItemCode is I2 D1 is CC D2 is DD
2022-04-30 15:32:30.033 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE1Z: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.037 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2A: ID is 1 Server is S1 ItemCode is I1 D1 is AAAA D2 is BBBB
2022-04-30 15:32:30.037 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2A: ID is 2 Server is S1 ItemCode is I2 D1 is CC D2 is DD
2022-04-30 15:32:30.037 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2A: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.049 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2B: ID is 2 Server is S1 ItemCode is I2 D1 is CC D2 is DD
2022-04-30 15:32:30.049 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2B: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.049 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2B: ID is 4 Server is S1 ItemCode is I1 D1 is AAAAAA D2 is BBBBBB
2022-04-30 15:32:30.054 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE3: ID is 2 Server is S1 ItemCode is I2 D1 is CCCCCC D2 is DDDDDD
2022-04-30 15:32:30.054 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE3: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.054 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE3: ID is 4 Server is S1 ItemCode is I1 D1 is AAAAAA D2 is BBBBBB
- 第一个块是对象,id 更新为插入生成的 id。
- 第二个块是从数据库中提取的数据
- 第三块使用REPLACE策略插入时指定id列,由于行被删除,由于UNIQUE约束冲突,在插入之前,插入相同id的行
- 第四块显示不指定id时的效果,即删除原行,插入新行到REPLACE
- 第五块显示更新方法按预期工作。
我在一个 android 应用程序中工作,该应用程序将来自远程服务器的数据存储在房间数据库中,然后从 Room DB 本身显示数据。
我的问题是我使用
autoIncrement
作为主键字段,我想使用OnConflictStrategy.REPLACE
将旧数据替换为来自远程服务器的新数据,我已经编写了代码。但是如何替换数据因为总是
autoIncrement
id 会不同并且每次都会插入数据而不是替换。
But how data will get replaced because always autoIncrement id will be different and data will inserted everytime instead of replace. Noting that the id will then be changed.
要使替换策略起作用,您必须确定与数据关联的行。您将根据唯一标识该行的 id 以外的数据执行此操作。
但是,使用替换是过时且低效的,因为知道如何唯一标识行意味着您可以使用更新(不是 @Update
,而是 @Query("UPDATE thetable SET the_first_changed_column = the_new value, the_next_changed_column_if one = the the_new_value /* more comma separated columns */ WHERE a_where_clause_that_uniquely_identifies_row")
)。
@Update 基于传递对象的主键值。
REPLACE 是低效的,因为它通过删除现有行来工作,然后需要相应地更改索引,然后插入一个新行 (如下所示可能有如果已指定 id,则相同的 id(请参阅有关第三个块的注释)),这需要相应地更改索引。
因为有这个“其他数据”应该唯一标识一行,所以使用 @Entity
注释的 indicies =
参数在这些列上建立索引会很明显相应 Index 的 unique
参数为真。
- 举个例子,其中 lastTime 列和 lastMessage 列唯一标识一行,那么你可以
indices = [Index(value = ["lastTime","lastMessage"], unique = true, name ="optional_name_of_index")]
- 名称是可选的
工作Example/Demo
下面是一个演示上述大部分内容的示例。
它基于一个带注释的@Entity class 示例 :-
@Entity(
indices = [
Index( value = ["serverName","itemCode"], unique = true)
]
)
data class Example(
@PrimaryKey
var id: Long? = null,
val serverName: String, /* UID Part 1 */
val itemCode: String, /* UID part 2 */
var otherData1: String,
var otherData2: String
)
var 用于 id、otherData1 和 otherData2,因此可以更改值。
可以看出,已经为应该唯一的两列添加了索引。
请注意,不需要 autogenerate = true,实际上这是低效的,因为它引入了 SQLite AUTOINCREMENT 关键字。但是,您确实需要将 id 值设置为 NULL 而不是 0。
- link regrading AUTOINCREMENT 的第一段说 “AUTOINCREMENT 关键字强加了额外的 CPU、内存、磁盘 space 和磁盘I/O 开销,如果不是严格需要,应该避免。通常不需要。"
@Dao 注释 class AllDAO 用于插入,建议的@Query 用于根据唯一数据进行更新,@Query 用于提取数据:-
@Dao
interface AllDAO {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(example: Example): Long
@Query("UPDATE example SET otherData1=:newOtherData1, otherData2=:newOtherData2 WHERE serverName=:serverName AND itemCode=:itemCode")
fun update(serverName: String, itemCode: String, newOtherData1: String, newOtherData2: String): Int
@Query("SELECT * FROM example")
fun getAllExamples(): List<Example>
}
- 注意 return 值
- 插入的 long 将是生成的 id
- update出来的Int会是更新的行数,虽然这个没有取到,应该是1
@Database 注释 class 以便实际演示 运行 可以是 运行 :-
@Database(entities = [Example::class], version = 1, exportSchema = false)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDAO(): AllDAO
companion object {
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
最后 activity 代码:-
- 插入一些数据
- 使用 REPLACE 更新数据
- 使用建议的 UPDATE 更新数据
- 从数据库中提取数据,在某些情况下从当前的示例对象中提取数据,以了解正在发生的事情
:-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDAO()
var ex1 = Example(serverName = "S1", itemCode = "I1", otherData1 = "AA", otherData2 = "BB")
var ex2 = Example(serverName = "S1", itemCode = "I2", otherData1 = "CC", otherData2 = "DD")
var ex3 = Example(serverName = "S1", itemCode = "I3", otherData1 = "EE", otherData2 = "FF")
ex1.id = dao.insert(ex1)
logAnExampleObject(ex1,"STAGE_1A") /* write the ex1 object to the log NOT from the db */
ex2.id = dao.insert(ex2)
logAnExampleObject(ex2,"STAGE_1B")
ex3.id = dao.insert(ex3)
logAnExampleObject(ex3,"STAGE_1C")
logAllExamples("_STAGE1Z") /* write all examples as extracted from the db */
ex1.otherData1 = "AAAA"
ex1.otherData2 = "BBBB"
dao.insert(ex1) /* NOTE ID AS PER INSERT due to updating value when inserted*/
logAllExamples("_STAGE2A")
ex1.id = null
ex1.otherData1 = "AAAAAA"
ex1.otherData2 = "BBBBBB"
dao.insert(ex1) /* NOTE ID set to NULL so as to use generated id */
logAllExamples("_STAGE2B")
dao.update("S1","I2","CCCCCC","DDDDDD")
logAllExamples("_STAGE3")
}
fun logAllExamples(prefix: String) {
for (ex in dao.getAllExamples()) {
logAnExampleObject(ex,prefix)
}
}
private fun logAnExampleObject(ex: Example, prefix: String) {
Log.d("USERINFO${prefix}","ID is ${ex.id} Server is ${ex.serverName} ItemCode is ${ex.itemCode} D1 is ${ex.otherData1} D2 is ${ex.otherData2}")
}
}
结果
第一次运行时,日志包括:-
2022-04-30 15:32:30.017 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFOSTAGE_1A: ID is 1 Server is S1 ItemCode is I1 D1 is AA D2 is BB
2022-04-30 15:32:30.019 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFOSTAGE_1B: ID is 2 Server is S1 ItemCode is I2 D1 is CC D2 is DD
2022-04-30 15:32:30.024 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFOSTAGE_1C: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.033 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE1Z: ID is 1 Server is S1 ItemCode is I1 D1 is AA D2 is BB
2022-04-30 15:32:30.033 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE1Z: ID is 2 Server is S1 ItemCode is I2 D1 is CC D2 is DD
2022-04-30 15:32:30.033 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE1Z: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.037 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2A: ID is 1 Server is S1 ItemCode is I1 D1 is AAAA D2 is BBBB
2022-04-30 15:32:30.037 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2A: ID is 2 Server is S1 ItemCode is I2 D1 is CC D2 is DD
2022-04-30 15:32:30.037 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2A: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.049 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2B: ID is 2 Server is S1 ItemCode is I2 D1 is CC D2 is DD
2022-04-30 15:32:30.049 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2B: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.049 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE2B: ID is 4 Server is S1 ItemCode is I1 D1 is AAAAAA D2 is BBBBBB
2022-04-30 15:32:30.054 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE3: ID is 2 Server is S1 ItemCode is I2 D1 is CCCCCC D2 is DDDDDD
2022-04-30 15:32:30.054 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE3: ID is 3 Server is S1 ItemCode is I3 D1 is EE D2 is FF
2022-04-30 15:32:30.054 6973-6973/a.a.so72006844kotlinroomrelations D/USERINFO_STAGE3: ID is 4 Server is S1 ItemCode is I1 D1 is AAAAAA D2 is BBBBBB
- 第一个块是对象,id 更新为插入生成的 id。
- 第二个块是从数据库中提取的数据
- 第三块使用REPLACE策略插入时指定id列,由于行被删除,由于UNIQUE约束冲突,在插入之前,插入相同id的行
- 第四块显示不指定id时的效果,即删除原行,插入新行到REPLACE
- 第五块显示更新方法按预期工作。