From 15448ac4cdbc6c61305c7f387356fc0494a62110 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 26 Jun 2024 18:32:44 +0530 Subject: [PATCH] Fixed: SQLiteConstraintException when inserting the same id item of hisotry/notes in room database. * id is the primary key for notes/history and when there is an already id available in the room database, and if there is the same id present in the objectbox database and we try to put that entity in the room it gives the SQLiteConstraintException. To fix this we have modified our saving functions of history and notes. If there is already an ID that exists in the room database that we are trying to put in the database it will set the ID to 0 so that the room will automatically assign the ID to that entity and our history/notes will prevent to be lost. * Added the test cases to properly test these scenarios. --- .../ObjectBoxToRoomMigratorTest.kt | 28 +++++++++++++++++++ .../kiwixmobile/roomDao/HistoryRoomDaoTest.kt | 20 +++++++++++++ .../kiwixmobile/roomDao/NoteRoomDaoTest.kt | 19 ++++++++++++- .../kiwixmobile/core/dao/HistoryRoomDao.kt | 10 ++++++- .../kiwixmobile/core/dao/NotesRoomDao.kt | 10 ++++++- 5 files changed, 84 insertions(+), 3 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt index 195dd193ca..55ab689d5f 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt @@ -239,6 +239,20 @@ class ObjectBoxToRoomMigratorTest { clearRoomAndBoxStoreDatabases(box) + // Test to insert the items with same id. + val historyItem4 = getHistoryItem( + databaseId = 2, + title = "Main Page", + historyUrl = "https://kiwix.app/A/MainPage" + ) + kiwixRoomDatabase.historyRoomDao().saveHistory(historyItem4) + box.put(HistoryEntity(historyItem)) + objectBoxToRoomMigrator.migrateHistory(box) + actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().blockingFirst() + assertEquals(2, actualData.size) + + clearRoomAndBoxStoreDatabases(box) + // Test migration if ObjectBox has null values try { lateinit var invalidHistoryEntity: HistoryEntity @@ -348,6 +362,20 @@ class ObjectBoxToRoomMigratorTest { clearRoomAndBoxStoreDatabases(box) + // Test to insert the items with same id. + val noteItem2 = getNoteListItem( + databaseId = 1, + zimUrl = "http://kiwix.app/Installing", + noteFilePath = "/storage/emulated/0/Download/Notes/Alpine linux/Installing.txt" + ) + kiwixRoomDatabase.notesRoomDao().saveNote(noteItem1) + box.put(NotesEntity(noteItem2)) + objectBoxToRoomMigrator.migrateNotes(box) + notesList = kiwixRoomDatabase.notesRoomDao().notes().blockingFirst() as List + assertEquals(2, notesList.size) + + clearRoomAndBoxStoreDatabases(box) + // Test migration if ObjectBox has null values try { lateinit var invalidNotesEntity: NotesEntity diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/roomDao/HistoryRoomDaoTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/roomDao/HistoryRoomDaoTest.kt index 33dd111785..0775fc4a29 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/roomDao/HistoryRoomDaoTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/roomDao/HistoryRoomDaoTest.kt @@ -101,6 +101,26 @@ class HistoryRoomDaoTest { assertThat(historyList.size, equalTo(1)) historyRoomDao.deleteAllHistory() + // Save two entity same data and database id but with different date + historyRoomDao.saveHistory(historyItem) + val historyItem1 = getHistoryItem( + title = "Main Page", + historyUrl = "https://kiwix.app/A/MainPage", + databaseId = 1, + dateString = "31 May 2024" + ) + historyRoomDao.saveHistory(historyItem1) + historyList = historyRoomDao.historyRoomEntity().blockingFirst() + assertThat(historyList.size, equalTo(2)) + historyRoomDao.deleteAllHistory() + + // Save two entity with same and database id with same date to see if it's updated or not. + historyRoomDao.saveHistory(historyItem) + historyRoomDao.saveHistory(historyItem) + historyList = historyRoomDao.historyRoomEntity().blockingFirst() + assertThat(historyList.size, equalTo(1)) + historyRoomDao.deleteAllHistory() + // Attempt to save undefined history item lateinit var undefinedHistoryItem: HistoryListItem.HistoryItem try { diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/roomDao/NoteRoomDaoTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/roomDao/NoteRoomDaoTest.kt index 8f3d9b0ce7..8dbcf4dc94 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/roomDao/NoteRoomDaoTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/roomDao/NoteRoomDaoTest.kt @@ -22,7 +22,6 @@ import android.content.Context import androidx.room.Room import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.core.IsEqual.equalTo @@ -110,6 +109,24 @@ class NoteRoomDaoTest { assertEquals(notesList.size, 1) clearNotes() + // Test to insert the items with same id. + val noteItem2 = getNoteListItem( + databaseId = 1, + zimUrl = "http://kiwix.app/Installing", + noteFilePath = "/storage/emulated/0/Download/Notes/Alpine linux/Installing.txt" + ) + val noteItem3 = getNoteListItem( + databaseId = 1, + title = "Installing", + zimUrl = "http://kiwix.app/Installing", + noteFilePath = "/storage/emulated/0/Download/Notes/Alpine linux/Installing.txt" + ) + kiwixRoomDatabase.notesRoomDao().saveNote(noteItem2) + kiwixRoomDatabase.notesRoomDao().saveNote(noteItem3) + notesList = notesRoomDao.notes().blockingFirst() as List + assertEquals(2, notesList.size) + clearNotes() + // Attempt to save undefined history item lateinit var undefinedNoteListItem: NoteListItem try { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/HistoryRoomDao.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/HistoryRoomDao.kt index 6221bfdadb..0741841169 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/HistoryRoomDao.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/HistoryRoomDao.kt @@ -51,6 +51,9 @@ abstract class HistoryRoomDao : PageDao { @Insert abstract fun insertHistoryItem(historyRoomEntity: HistoryRoomEntity) + @Query("SELECT COUNT() FROM HistoryRoomEntity WHERE id = :id") + abstract fun count(id: Int): Int + fun saveHistory(historyItem: HistoryListItem.HistoryItem) { getHistoryRoomEntity( historyItem.historyUrl, @@ -65,7 +68,12 @@ abstract class HistoryRoomDao : PageDao { } updateHistoryItem(it) } ?: run { - insertHistoryItem(HistoryRoomEntity(historyItem)) + val historyEntity = HistoryRoomEntity(historyItem) + if (count(historyEntity.id.toInt()) > 0) { + // set the default id so that room will automatically generates the database id. + historyEntity.id = 0 + } + insertHistoryItem(historyEntity) } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NotesRoomDao.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NotesRoomDao.kt index a8ac791c9a..461de6173d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NotesRoomDao.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NotesRoomDao.kt @@ -38,9 +38,17 @@ abstract class NotesRoomDao : PageDao { deleteNotes(pagesToDelete as List) fun saveNote(noteItem: NoteListItem) { - saveNote(NotesRoomEntity(noteItem)) + val notesEntity = NotesRoomEntity(noteItem) + if (count(notesEntity.id.toInt()) > 0) { + // set the default id so that room will automatically generates the database id. + notesEntity.id = 0 + } + saveNote(notesEntity) } + @Query("SELECT COUNT() FROM NotesRoomEntity WHERE id = :id") + abstract fun count(id: Int): Int + @Insert(onConflict = OnConflictStrategy.REPLACE) abstract fun saveNote(notesRoomEntity: NotesRoomEntity)