【问题标题】:Getting "UNIQUE constraint failed" when using @Upsert in Android Room在 Android Room 中使用@Upsert 时出现“UNIQUE constraint failed”
【发布时间】:2023-01-12 22:36:07
【问题描述】:

获取 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>)

为什么我会收到唯一约束错误。现有记录不会被更新而不是重新插入吗?

从安卓文档:

如果目标实体包含自动生成的主键,则 POJO 类不需要相等的主键字段,否则主键也必须存在于 POJO 中。如果主键已经存在,则只更新部分实体字段所代表的列

【问题讨论】:

    标签: android android-room upsert


    【解决方案1】:

    我对@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))
        }
    }
    

    结果成功运行,App Inspection 显示:-

    • 即它已经处理了 4 个 UPSERTS,结果数据符合预期。

    这个问题可能在其他地方。可能是因为您似乎在使用 sqlcipher 或其他数据库操作。

    【讨论】:

    • 不幸的是,我需要使用 SQLcipher 和类型转换器。此外,它并没有真正回答这个问题,为什么它试图重新插入记录而不是仅仅更新它。另一件事,您是否一遍又一遍地运行插入以查看它们是否被重新插入或更新?但我真的很感谢你为帮助我所做的努力。
    • @KristyWelsh 并没有说它们是问题所在(类型转换器并不是纯粹为了测试的简单/方便而使用的),只是 sqlcipher 可能是(不应该是)。
    【解决方案2】:

    Android room 2.5.0 和 sqlcipher 之间似乎存在冲突。一旦我从我的房间数据库中删除了 sqlcipher SupportFactory,upsert 就会按预期工作。

    【讨论】:

      猜你喜欢
      • 2022-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-16
      • 2022-12-04
      • 2012-03-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多