Room数据库Projekt表删除异常:删除最后一条笔记后项目未移除
问题描述
从Room数据库的projekte表中删除指定项目。数据库包含projekte、locations、notizen三张表,其中notizen表与另外两张表存在外键关联。目前已实现删除笔记及项目的方法,笔记可正常删除,但删除最后一条笔记后,对应的项目并未从数据库中移除。
实体类与数据库定义代码
@Entity(tableName = "projekte") data class Projekt(@PrimaryKey val id: Long, var beschreibung: String?) { fun getDescription(): String { val dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT) // Import!! val dateString = dateFormat.format(id) return if (beschreibung.isNullOrEmpty()) dateString else "$beschreibung ($dateString)" } } @Entity (tableName = "locations", primaryKeys = ["latitude", "longitude"]) data class Location(val latitude: Double, val longitude: Double, val altitude: Double, val provider: String) @VersionedParcelize @Entity (tableName = "notizen", foreignKeys = [ForeignKey(entity = Projekt::class, parentColumns = ["id"], childColumns = ["projektId"]), ForeignKey(entity = Location::class, parentColumns = ["latitude", "longitude"], childColumns = ["latitude", "longitude"])]) data class Notiz(@PrimaryKey(autoGenerate = true) var id: Long? = null, val projektId: Long, val latitude: Double, val longitude: Double, var thema: String, var notiz: String):Parcelable { constructor(parcel: Parcel) : this( parcel.readValue(Long::class.java.classLoader) as? Long, parcel.readLong(), parcel.readDouble(), parcel.readDouble(), parcel.readString().toString(), parcel.readString().toString() ) { } override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeValue(id) parcel.writeLong(projektId) parcel.writeDouble(latitude) parcel.writeDouble(longitude) parcel.writeString(thema) parcel.writeString(notiz) } override fun describeContents(): Int { return 0 } companion object CREATOR : Parcelable.Creator<Notiz> { override fun createFromParcel(parcel: Parcel): Notiz { return Notiz(parcel) } override fun newArray(size: Int): Array<Notiz?> { return arrayOfNulls(size) } } } @Database(entities = [Projekt::class, Notiz::class, Location::class], version = 1) abstract class GeoNotesDatabase : RoomDatabase() { companion object { private var INSTANCE: GeoNotesDatabase? = null fun getInstance(context: Context): GeoNotesDatabase { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder(context, GeoNotesDatabase::class.java, "GeoNotesDatabase").build() } return INSTANCE as GeoNotesDatabase } } abstract fun projekteDao(): ProjekteDao abstract fun locationDao(): LocationDao abstract fun notizenDao(): NotizenDao } @Dao interface ProjekteDao { @Insert (onConflict = OnConflictStrategy.REPLACE) fun insertProjekt (projekt: Projekt): Long @Query("SELECT * FROM projekte") fun getProjekte() : List<Projekt> @Query ("SELECT * FROM projekte where id=:id") fun getProjekt(id: Long): Projekt @Query ("DELETE FROM projekte where id= :id") fun deleteProjekt(id: Long?) @Update fun updateProjekt(projekt: Projekt) } @Dao interface LocationDao { @Insert (onConflict = OnConflictStrategy.IGNORE) fun insertLocation(location: Location): Long @Query ("SELECT * FROM locations") fun getLocations(): List<Location> } @Dao interface NotizenDao { @Insert (onConflict = OnConflictStrategy.REPLACE) fun insertNotiz(notiz: Notiz): Long @Query ("SELECT * FROM notizen where projektId = :projektId") fun getNotizen(projektId: Long): List<Notiz> @Query ("SELECT * FROM notizen where projektId = :projektId AND id < :notizId ORDER BY id ASC") fun getPreviousNotizen(notizId: Long?, projektId: Long): List<Notiz> @Query ("SELECT * FROM notizen where projektId = :projektId AND id > :notizId ORDER BY id ASC") fun getNextNotizen(notizId: Long?, projektId: Long): List<Notiz> @Query ("SELECT * FROM notizen where id = :id") fun getNotiz(id: Long?): Notiz @Query("DELETE FROM notizen where id = :id") fun deleteNotiz(id: Long?) }
删除方法代码
fun notizLoeschen() { //Notiz Löschen val textViewThema = findViewById<TextView>(R.id.edittext_thema) val textViewNotiz = findViewById<TextView>(R.id.edittext_notiz) var notizen: List<Notiz>? = null if (aktuelleNotiz == null) return val database = GeoNotesDatabase.getInstance(this) CoroutineScope(Dispatchers.Main).launch { withContext(Dispatchers.IO) { notizen = database.notizenDao().getNotizen(aktuellesProjekt.id) } if (notizen?.size!! > 1) { withContext(Dispatchers.IO) { database.notizenDao().deleteNotiz(aktuelleNotiz!!.id) } aktuelleNotiz = null textViewThema.text = "" textViewNotiz.text = "" return@launch }else { with(AlertDialog.Builder(this@GatherActivity)) { setTitle("Löschen der letzten Notiz löscht das Projekt. Forfahren?") setPositiveButton("OK", DialogInterface.OnClickListener { dialog, id -> CoroutineScope(Dispatchers.Main).launch { withContext(Dispatchers.IO) { aktuelleNotiz = notizen!!.last() Log.d(javaClass.simpleName, "TEST P: ${aktuelleNotiz?.id} P: ${aktuellesProjekt.id}") // Testweise eine Ausgabe der beiden ID's database.notizenDao().deleteNotiz(aktuelleNotiz!!.id) database.projekteDao().deleteProjekt(aktuellesProjekt!!.id) aktuellesProjekt = Projekt(Date().time, "") database.projekteDao().insertProjekt(aktuellesProjekt) } } aktuellesProjekt = Projekt(Date().time, "") findViewById<TextView>(R.id.textview_aktuelles_projekt).text = getString(R.string.aktuelles_projekt_prefix) + aktuellesProjekt.getDescription() aktuelleNotiz = null textViewThema.text = "" textViewNotiz.text = "" }) setNegativeButton( "ABBRECHEN", DialogInterface.OnClickListener { dialog, id -> }) show() } } } }
问题分析与修复方案
核心问题
代码逻辑存在矛盾:删除最后一条笔记和对应项目后,立刻创建了一个新的Projekt并插入到数据库,导致数据库里始终存在项目,看起来像是原项目未被删除。
修复步骤
移除多余的项目插入代码
在IO协程中,删除完笔记和项目后,删掉以下两行代码:aktuellesProjekt = Projekt(Date().time, "") database.projekteDao().insertProjekt(aktuellesProjekt)修改后协程内代码:
withContext(Dispatchers.IO) { aktuelleNotiz = notizen!!.last() Log.d(javaClass.simpleName, "TEST P: ${aktuelleNotiz?.id} P: ${aktuellesProjekt.id}") database.notizenDao().deleteNotiz(aktuelleNotiz!!.id) database.projekteDao().deleteProjekt(aktuellesProjekt!!.id) }这样旧项目会被彻底删除,不会有新项目自动插入。
优化外键约束(可选但推荐)
当前Notiz表的外键未设置级联删除,需要手动删除笔记后再删项目。可以给外键添加onDelete = ForeignKey.CASCADE,删除Projekt时关联的Notiz会自动删除,简化代码:
修改Notiz实体的外键定义:foreignKeys = [ForeignKey(entity = Projekt::class, parentColumns = ["id"], childColumns = ["projektId"], onDelete = ForeignKey.CASCADE), // 添加级联删除 ForeignKey(entity = Location::class, parentColumns = ["latitude", "longitude"], childColumns = ["latitude", "longitude"])]修正UI更新逻辑
原代码在协程外重复创建aktuellesProjekt,会导致UI显示与数据库状态不一致。建议将UI更新代码移到协程的withContext(Dispatchers.Main)中,确保状态同步:CoroutineScope(Dispatchers.Main).launch { withContext(Dispatchers.IO) { // 数据库操作 } // 同步更新UI aktuellesProjekt = null findViewById<TextView>(R.id.textview_aktuelles_projekt).text = getString(R.string.aktuelles_projekt_prefix) + "无项目" aktuelleNotiz = null textViewThema.text = "" textViewNotiz.text = "" }
内容的提问来源于stack exchange,提问作者Andreas Kirchberger
相关产品推荐
相关产品推荐

