获取
net.sqlcipher.database.SQLiteConstraintException: error code 19: UNIQUE constraint failed with : appointment.activity_id
:尝试使用 Android Room 进行 @Upsert 时。
我对@Upsert的理解是,如果行存在,它将更新记录,如果不存在,它将插入。
以下是该表的定义:
data class AppointmentEntity(
@PrimaryKey
@field:ColumnInfo(name = "activity_id")
val activityId: Long,
@field:ColumnInfo(name = "selected_owner")
val selectedOwner: String,
val activityStartTimestamp: Instant,
val activityEndTimestamp: Instant,
val activityRevisionTimestamp: Instant?,
val appointmentTypeCode: AppointmentTypeCode,
val contactName: String?,
val activityDescription: String?,
val recurringId: Long?,
val cancelled: Boolean,
val personalAppointment: Boolean,
val outlookIndicator: Boolean,
val allDayEvent: Boolean,
val deleteDate: Instant?
)
这是@Dao中的代码:
@Upsert
fun insertAppointments(appointment: List<AppointmentEntity>)
为什么我会收到唯一约束错误。现有记录不是会被更新而不是重新插入吗?
来自 Android 文档:
如果目标实体包含自动生成的 PrimaryKey,则 POJO 类不需要相同的主键字段,否则 POJO 中也必须存在主键。如果主键已经存在,则只会更新部分实体字段代表的列
Android room 2.5.0 和 sqlcipher 之间似乎存在冲突。当我从我的房间数据库中删除 sqlcipher SupportFactory 后,upsert 就按预期工作了。
这似乎是根本原因:https://github.com/sqlcipher/android-database-sqlcipher/issues/588。 SqlCipher 不会扩展 android.database.SQLException,因此当 Room 检查异常是否是外键/唯一键时:https://android-review.googlesource.com/c/platform/frameworks/support/+/2191947/ 9/room/room-runtime/src/main/java/androidx/room/EntityUpsertionAdapter.kt#209 它总是抛出 net.sqlcipher.database.SQLException 唯一键异常,而不是更新现有记录。
我对@Upsert的理解是,如果行存在,它将更新记录,如果不存在,它将插入。
它应该使用您可用的代码,但修改为不使用类型转换器,它确实如此。例如使用:-
@Entity(tableName = "appointment")
data class AppointmentEntity(
@PrimaryKey
@field:ColumnInfo(name = "activity_id")
val activityId: Long,
@field:ColumnInfo(name = "selected_owner")
val selectedOwner: String,
val activityStartTimestamp: Long?=System.currentTimeMillis() / 1000, /*<<<< CHANGED FROM Instant */
val activityEndTimestamp: Long?=System.currentTimeMillis() / 1000, /*<<<< CHANGED FROM Instant */
val activityRevisionTimestamp: Long?=System.currentTimeMillis() / 1000, /*<<<< CHANGED FROM Instant */
//val appointmentTypeCode: AppointmentTypeCode, /*<<<<< COMMENTED OUT */
val contactName: String?,
val activityDescription: String?,
val recurringId: Long?,
val cancelled: Boolean,
val personalAppointment: Boolean,
val outlookIndicator: Boolean,
val allDayEvent: Boolean,
val deleteDate: Long?=null /*<<<< CHANGED FROM Instant */
)
@Database(entities = [AppointmentEntity::class], exportSchema = false, version = 1)
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
}
}
}
@Dao
interface AllDao {
@Upsert
fun insertAppointments(appointment: List<AppointmentEntity>)
}
和:-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var appointmentEntity1 = AppointmentEntity(activityId = 1, selectedOwner = "O001", contactName = "C001", recurringId = 0, cancelled = false, activityDescription = "Desc001", personalAppointment = true, outlookIndicator = false, allDayEvent = false)
var appointmentEntity2 = AppointmentEntity(activityId = 1, selectedOwner = "O001", contactName = "C002", recurringId = 0, cancelled = false, activityDescription = "Desc002", personalAppointment = true, outlookIndicator = false, allDayEvent = false)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
dao.insertAppointments(listOf(appointmentEntity1,appointmentEntity2,appointmentEntity1,appointmentEntity2))
}
}
应用程序检查成功运行的结果显示:-
问题可能出在其他地方。也许是因为您似乎正在使用 sqlcipher 或其他数据库操作。
如果您使用的是 room 版本 < 2.6.0, it could be related with a bug fix they did in this release: https://developer.android.com/jetpack/androidx/releases/room#2.6.0-beta01