I followed this post Android Room FOREIGN KEY constraint failed (code 787). Unfortunately, it not works in my case.
I always get the error when trying to insert a new Note. Of course, there is a Topic existed in Database.
Do you guys have any idea?
Please take a look at my code below.
Topic:
#Entity(tableName = "topic")
data class Topic(#PrimaryKey var id: Long? = null,
#ColumnInfo(name = "name") var name: String,
#ColumnInfo(name = "note_count") var noteCount: Int = 0,
#ColumnInfo(name = "view_count") var viewCount: Long = 0) : Serializable {
#Ignore
var notes: List<Note>? = null
}
Note:
#Entity(tableName = "note",
foreignKeys = arrayOf(ForeignKey(entity = Topic::class,
parentColumns = arrayOf("id"), childColumns = arrayOf("topic_id"), onDelete = ForeignKey.CASCADE)))
#TypeConverters(TimestampConverter::class)
data class Note(#PrimaryKey(autoGenerate = true) var noteId: Long? = null,
#ColumnInfo(name = "topic_id") var topicId: Long? = null,
#ColumnInfo(name = "title") var title: String,
#ColumnInfo(name = "caption") var caption: String? = "",
#ColumnInfo(name = "created_at") var createdAt: Date? = null) : Serializable {
}
NoteDao:
#Dao
interface NoteDao {
#Query("SELECT * from note")
fun getAll(): LiveData<List<Note>>
#Insert(onConflict = REPLACE)
fun insert(note: Note)
#Query("DELETE from note")
fun deleteAll()
#Delete
fun delete(category: Note)
#Query("SELECT * FROM note WHERE title = :title COLLATE NOCASE LIMIT 1")
fun findNoteByTitle(title: String): Note?
#Query("SELECT * FROM note WHERE topic_id = :topicId")
fun findNotesByTopicId(topicId: Long): LiveData<List<Note>>
#Query("SELECT count(*) FROM note WHERE topic_id = :topicId")
fun countNotesByTopicId(topicId: Long): Long
#Update(onConflict = IGNORE)
fun update(category: Note)
}
I found the problem. Because I have been created 2 different Databases: TopicDataBase and NoteDataBase.
So I just need to remove 1 of them.
My bad >"<
(#PrimaryKey var id: Long? = null, looks like a problem. Primary key can't be null
Related
I have a requirement for two different Room tables with the same ShoppingListItem Entity. So, I recreated another Entity data class model with the same properties but with a different name, CustomSortShoppingListItemEntity. But, Room throws the error stating that there is no such table which is specified as custom_sort_shopping_list_items in the CustomSortShoppingListItemEntity data class. Do I have to create a separate Dao for the custom_sort_shopping_list_items table? How can I fix this?
Error message
error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such table: custom_sort_shopping_list_items)
public abstract java.lang.Object countCustomSortListItems(long listId, #org.jetbrains.annotations.NotNull()
ShoppingListItemEntity
#Entity(
tableName = "shopping_list_items",
foreignKeys = [ForeignKey(
entity = ShoppingListEntity::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("shopping_list_id"),
onUpdate = ForeignKey.CASCADE,
onDelete = ForeignKey.CASCADE
)]
)
data class ShoppingListItemEntity(
#PrimaryKey(autoGenerate = true)
var id: Long = 0L,
#ColumnInfo(name = "shopping_list_id")
var shoppingListId: Long = 0L,
#ColumnInfo(name = "name")
val name: String,
#ColumnInfo(name = "category")
val category: String,
#ColumnInfo(name = "quantity")
val quantity: String,
#ColumnInfo(name = "set_quantity")
val setQuantity: String,
#ColumnInfo(name = "set_total")
val setTotal: String,
#ColumnInfo(name = "unit")
val unit: String,
#ColumnInfo(name = "item_ppu")
val itemPPU: String,
#ColumnInfo(name = "item_set_ppu")
val itemSetPPU: String,
#ColumnInfo(name = "notes")
val notes: String,
#ColumnInfo(name = "coupon_amount")
val couponAmount: String,
#ColumnInfo(name = "set_coupon_amount")
val setCouponAmount: String,
#ColumnInfo(name = "item_total")
val itemTotal: String,
#ColumnInfo(name = "item_set_total")
val itemSetTotal: String,
#ColumnInfo(name = "image_uri")
val itemImageUri: String?,
#ColumnInfo(name = "thumbnail_uri")
val itemThumbnailUri: String?,
#ColumnInfo(name = "is_in_cart")
val isInCart: Boolean
)
CustomSortShoppingListItemEntity
#Entity(
tableName = "custom_sort_shopping_list_items",
foreignKeys = [ForeignKey(
entity = ShoppingListEntity::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("shopping_list_id"),
onUpdate = ForeignKey.CASCADE,
onDelete = ForeignKey.CASCADE
)]
)
data class CustomSortShoppingListItemEntity(
#PrimaryKey(autoGenerate = true)
var id: Long = 0L,
#ColumnInfo(name = "shopping_list_id")
var shoppingListId: Long = 0L,
#ColumnInfo(name = "name")
val name: String,
#ColumnInfo(name = "category")
val category: String,
#ColumnInfo(name = "quantity")
val quantity: String,
#ColumnInfo(name = "set_quantity")
val setQuantity: String,
#ColumnInfo(name = "set_total")
val setTotal: String,
#ColumnInfo(name = "unit")
val unit: String,
#ColumnInfo(name = "item_ppu")
val itemPPU: String,
#ColumnInfo(name = "item_set_ppu")
val itemSetPPU: String,
#ColumnInfo(name = "notes")
val notes: String,
#ColumnInfo(name = "coupon_amount")
val couponAmount: String,
#ColumnInfo(name = "set_coupon_amount")
val setCouponAmount: String,
#ColumnInfo(name = "item_total")
val itemTotal: String,
#ColumnInfo(name = "item_set_total")
val itemSetTotal: String,
#ColumnInfo(name = "image_uri")
val itemImageUri: String?,
#ColumnInfo(name = "thumbnail_uri")
val itemThumbnailUri: String?,
#ColumnInfo(name = "is_in_cart")
val isInCart: Boolean
)
Shopping List Items Dao
#Dao
interface ShoppingListItemDao {
//===== SHOPPING LIST TABLE =====//
//Create
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertShoppingListItem(item: ShoppingListItemEntity)
//Read
#Query("SELECT (SELECT COUNT(*) FROM shopping_list_items WHERE shopping_list_id = :listId)")
suspend fun countListItems(listId: Long): Int
#Query("SELECT * FROM shopping_list_items WHERE shopping_list_id = :listId")
fun getAllShoppingListItems(listId: Long): PagingSource<Int, ShoppingListItemEntity>
#Query("SELECT * FROM shopping_list_items WHERE id = :id")
suspend fun getShoppingListItemById(id: Long): ShoppingListItemEntity
//Update
#Update(entity = ShoppingListItemEntity::class)
suspend fun updateShoppingListItem(item: ShoppingListItemEntity)
//Delete
#Delete
suspend fun deleteShoppingListItem(item: ShoppingListItemEntity)
//===== CUSTOM SORT TABLE =====//
//Create
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCustomSortShoppingListItem(item: CustomSortShoppingListItemEntity)
//Read --> Throws error here
#Query("SELECT (SELECT COUNT(*) FROM custom_sort_shopping_list_items WHERE shopping_list_id = :listId)")
suspend fun countCustomSortListItems(listId: Long): Int
#Query("SELECT * FROM custom_sort_shopping_list_items WHERE shopping_list_id = :listId")
fun getAllCustomSortShoppingListItems(listId: Long): PagingSource<Int, CustomSortShoppingListItemEntity>
//Update
#Update(entity = ShoppingListItemEntity::class)
suspend fun updateCustomSortShoppingListItem(item: CustomSortShoppingListItemEntity)
//Delete
#Delete
suspend fun deleteCustomSortShoppingListItem(item: CustomSortShoppingListItemEntity)
//Update All
#Query("DELETE FROM custom_sort_shopping_list_items WHERE shopping_list_id = :listId")
suspend fun deleteAllCustomSortShoppingListItems(listId: Long)
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAllCustomSortShoppingListItems(customItems: List<CustomSortShoppingListItemEntity>)
}
I am currently trying to get results from a query of nested relationships in Room. Here Are my classes/database entities involved:
#Entity
data class PrayerRequestEntity(
var title: String,
var details: String,
var category: String,
var isAnswered: Boolean,
var prayerCount: Int,
var dateCreated: Date,
var dateUpdated: Date
) {
#PrimaryKey(autoGenerate = true)
var prayerRequestId: Int = 0
}
#Entity
data class PrayerPlanEntity(
var title: String,
var sessionCount: Int,
var dateCreated: Date,
var dateUpdated: Date
) {
#PrimaryKey(autoGenerate = true)
var planId: Int = 0
}
#Entity(primaryKeys = ["planId", "prayerRequestId"])
data class PrayerPlanPrayerRequestJoinEntity(
val planId: Int,
val prayerRequestId: Int
)
I am trying to get a list of PrayerPlanEtities along with the PrayerRequestEntities associated with each and have no problem using the following return:
data class PrayerPlanPrayerRequestsJoin(
#Embedded
val prayerPlan: PrayerPlanEntity,
#Relation(
parentColumn = "planId",
entityColumn = "prayerRequestId",
associateBy = Junction(PrayerPlanPrayerRequestJoinEntity::class)
)
val prayers: List<PrayerPlanEntity>
)
I need to also query the relationships of PrayerRequestEntity in that same query so Imodify the aforementioned class like so:
data class PrayerPlanPrayerRequestsJoin(
#Embedded
val prayerPlan: PrayerPlanEntity,
#Relation(
parentColumn = "planId",
entityColumn = "prayerRequestId",
associateBy = Junction(PrayerPlanPrayerRequestJoinEntity::class)
)
val prayers: List<PrayerRequestJoin>
)
PrayerRequestJoin is a previous relationship I created which works perfectly fine on it own and looks like so:
data class PrayerRequestJoin(
#Embedded
val prayerRequest: PrayerRequestEntity,
#Relation(
parentColumn = "prayerRequestId",
entityColumn = "prayerRequestCategoryId",
associateBy = Junction(PrayerRequestCategoryJoinEntity::class)
)
val category: PrayerRequestCategoryEntity,
#Relation(
parentColumn = "prayerRequestId",
entityColumn = "photoId",
associateBy = Junction(PrayerRequestPhotoJoinEntity::class)
)
val photos: List<PhotoEntity>,
#Relation(
parentColumn = "prayerRequestId",
entityColumn = "noteId",
associateBy = Junction(PrayerRequestNoteJoinEntity::class)
)
val notes: List<NoteEntity>
)
But now I am getting two build errors an Android Studio:
error: constructor PrayerPlanPrayerRequestsJoin in class PrayerPlanPrayerRequestsJoin cannot be applied to given types;
_item = new PrayerPlanPrayerRequestsJoin();
^
required: PrayerPlanEntity,List
found: no arguments
reason: actual and formal argument lists differ in length
and also:
error: prayerPlan has private access in PrayerPlanPrayerRequestsJoin
_item.prayerPlan = _tmpPrayerPlan;
Can anyone provide some insight on what may be causing this issue?
I believe that your issue is with the #Query's rather than with the relationships.
Upon closer inspection the messages shows java code e.g. termination with ;'s.
Therefore the issue is within the generated java and it would appear (from creating a working example) that the issue is in the code underlying the class or classes annotated with #Dao
i.e. _item does not appear in the java for the class annotated with #Database. Whilst _item = new .... appears for each #Query annotated function. e.g.
for a Query using SELECT * FROM PrayerPlanPrayerRequestJoinEntity then the code includes _item = new PrayerPlanPrayerRequestJoinEntity(_tmpPlanId,_tmpPrayerRequestId);
for a Query using "SELECT * FROM prayerRequestEntity" then the code includes _item = new PrayerRequestJoin(_tmpPrayerRequest,_tmpCategory_1,_tmpPhotosCollection_1,_tmpNotesCollection_1);
As can be seen these are used to build the final result. The first example being the closest from the working example but there is no issue.
As such I believe that the issue is not with the relationships (working example runs OK) but the issue is with the #Query annotated functions.
I would suggest commenting them all out and adding each until you find the culprit.
The working example
Note that this example introduces/uses some shortcuts so the code differs a little.
the code is run on the main thread so uses .allowMainThreadQueries
Long's are used instead of Ints for id's (they should really always be longs as they can be signed 64 bit)
Autogenerate has not been used for classes that have been guessed such as NoteEntity (autogenerate (which equates to AUTOINCREMENT) is actually not recommended by SQLite themselves The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed. https://sqlite.org/autoinc.html)
TypeConverters have been avoided
The code:-
NoteEntity made up just to test, so very simple
#Entity
data class NoteEntity(
#PrimaryKey
var noteId: Long? = null,
var noteText: String = ""
)
PhotoEntity made up ....
#Entity
data class PhotoEntity(
#PrimaryKey
var photoId: Long? = null,
var photoImage: String = "unknown"
)
PrayerPlanEntity
#Entity
data class PrayerPlanEntity(
var title: String,
var sessionCount: Int,
var dateCreated: String, /* changed for brevity */
var dateUpdated: String /* changed for brevity */
) {
#PrimaryKey(autoGenerate = true)
var planId: Long = 0
}
PrayerPlanPrayerRequestJoinEntity
#Entity(primaryKeys = ["planId", "prayerRequestId"])
data class PrayerPlanPrayerRequestJoinEntity(
val planId: Long,
val prayerRequestId: Long
)
PrayerPlanPrayerRequestsJoin assume the 2nd is as it is now
data class PrayerPlanPrayerRequestsJoin(
/*
#Embedded
val prayerPlan: PrayerPlanEntity,
#Relation(
parentColumn = "planId",
entityColumn = "prayerRequestId",
associateBy = Junction(PrayerPlanPrayerRequestJoinEntity::class)
)
val prayers: List<PrayerPlanEntity>
*/
#Embedded
val prayerPlan: PrayerPlanEntity,
#Relation(
parentColumn = "planId",
entityColumn = "prayerRequestId",
associateBy = Junction(PrayerPlanPrayerRequestJoinEntity::class)
)
val prayers: List<PrayerRequestJoin>
)
PrayerRequestCategoryEntity made up ....
#Entity
data class PrayerRequestCategoryEntity(
#PrimaryKey
var prayerRequestCategoryId: Long? = null,
var categoryName: String
)
PrayerRequestCategoryJoinEntity made up ....
#Entity(primaryKeys = ["prayerRequestId", "prayerRequestCategoryId"])
data class PrayerRequestCategoryJoinEntity(
val prayerRequestId: Long,
val prayerRequestCategoryId: Long
)
PrayerRequestEntity
#Entity
data class PrayerRequestEntity(
var title: String,
var details: String,
var category: String,
var isAnswered: Boolean,
var prayerCount: Int,
var dateCreated: String, /* changed for brevity */
var dateUpdated: String /* changed for brevity */
) {
#PrimaryKey(autoGenerate = true)
var prayerRequestId: Long = 0
}
PrayerRequestJoin
data class PrayerRequestJoin(
#Embedded
val prayerRequest: PrayerRequestEntity,
#Relation(
parentColumn = "prayerRequestId",
entityColumn = "prayerRequestCategoryId",
associateBy = Junction(PrayerRequestCategoryJoinEntity::class)
)
val category: PrayerRequestCategoryEntity,
#Relation(
parentColumn = "prayerRequestId",
entityColumn = "photoId",
associateBy = Junction(PrayerRequestPhotoJoinEntity::class)
)
val photos: List<PhotoEntity>,
#Relation(
parentColumn = "prayerRequestId",
entityColumn = "noteId",
associateBy = Junction(PrayerRequestNoteJoinEntity::class)
)
val notes: List<NoteEntity>
)
PrayerRequestNoteJoinEntity made up
#Entity(primaryKeys = ["prayerRequestId", "noteId"])
data class PrayerRequestNoteJoinEntity(
val prayerRequestId: Long,
val noteId: Long
)
PrayerRequestPhotoJoinEntity made up ....
#Entity(primaryKeys = ["prayerRequestId", "photoId"])
data class PrayerRequestPhotoJoinEntity(
val prayerRequestId: Long,
val photoId: Long
)
AllDAO made up
#Dao
abstract class AllDAO {
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(photoEntity: PhotoEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(noteEntity: NoteEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(prayerPlanEntity: PrayerPlanEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(prayerRequestCategoryEntity: PrayerRequestCategoryEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(prayerRequestEntity: PrayerRequestEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(prayerPlanPrayerRequestJoinEntity: PrayerPlanPrayerRequestJoinEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(prayerRequestCategoryJoinEntity: PrayerRequestCategoryJoinEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(prayerRequestNoteJoinEntity: PrayerRequestNoteJoinEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(prayerRequestPhotoJoinEntity: PrayerRequestPhotoJoinEntity): Long
#Query("SELECT * FROM prayerRequestEntity")
abstract fun getAllPrayerRequests(): List<PrayerRequestEntity>
#Query("SELECT * FROM prayerPlanEntity")
abstract fun getAllPrayerPlans(): List<PrayerPlanEntity>
#Query("SELECT * FROM PrayerPlanPrayerRequestJoinEntity")
abstract fun getAllPrayerPlanRequestJoins(): List<PrayerPlanPrayerRequestJoinEntity>
#Query("SELECT * FROM prayerRequestEntity")
abstract fun getAllPrayerRequestsWithCategoryNotesAndPhotos(): List<PrayerRequestJoin>
}
TheDatabase made up
#Database(entities = [
PhotoEntity::class,
NoteEntity::class,
PrayerRequestEntity::class,
PrayerPlanEntity::class,
PrayerRequestCategoryEntity::class,
PrayerRequestCategoryJoinEntity::class,
PrayerRequestNoteJoinEntity::class,
PrayerRequestPhotoJoinEntity::class,
PrayerPlanPrayerRequestJoinEntity::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,"prayer.db")
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
Last but not least an activity to do a simple test of the related data via the getAllPrayerRequestsWithCategoryNotesAndPhotos query:-
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()
val n1id = dao.insert(NoteEntity(noteText = "Note1"))
val n2id = dao.insert(NoteEntity(noteText = "Note2"))
val n3id = dao.insert(NoteEntity(noteText = "Note3"))
val photo1id = dao.insert(PhotoEntity(photoImage = "photo1"))
val photo2id = dao.insert(PhotoEntity(photoImage = "photo2"))
val photo3id = dao.insert(PhotoEntity(photoImage = "photo3"))
val pp1id = dao.insert(PrayerPlanEntity(title = "PP01",10,"today","never"))
val pcat01id = dao.insert(PrayerRequestCategoryEntity(categoryName = "CAT01"))
val pcat02id = dao.insert(PrayerRequestCategoryEntity(categoryName = "CAT02"))
val pcat03id = dao.insert(PrayerRequestCategoryEntity(categoryName = "CAT03"))
val pr01id =dao.insert(PrayerRequestEntity("PR01","details01","cat01",false,10,"today","never"))
dao.insert(prayerPlanPrayerRequestJoinEntity = PrayerPlanPrayerRequestJoinEntity(prayerRequestId = pr01id, planId = pp1id))
dao.insert(PrayerRequestPhotoJoinEntity(pr01id,photo1id))
dao.insert(PrayerRequestPhotoJoinEntity(pr01id,photo2id))
dao.insert(PrayerRequestPhotoJoinEntity(pr01id,photo3id))
dao.insert(PrayerRequestCategoryJoinEntity(pr01id,n1id))
dao.insert(PrayerRequestNoteJoinEntity(pr01id,n2id))
dao.insert(PrayerRequestNoteJoinEntity(pr01id,n3id))
dao.insert(PrayerRequestCategoryJoinEntity(pr01id,pcat01id))
/* Extract the Data and output to the log */
for (prj: PrayerRequestJoin in dao.getAllPrayerRequestsWithCategoryNotesAndPhotos()) {
Log.d("DBINFO","Prayer Request = ${prj.prayerRequest.details} \n\tRequestCategory = ${prj.category.categoryName} ID is ${prj.category.prayerRequestCategoryId}")
for (photos: PhotoEntity in prj.photos) {
Log.d("DBINFO","\tPhoto = ${photos.photoImage} ID = ${photos.photoId}")
}
for (notes: NoteEntity in prj.notes) {
Log.d("DBINFO","\tNote = ${notes.noteText} ID = ${notes.noteId}")
}
}
}
}
Finally the result from the Log:-
2022-03-24 12:52:35.758 D/DBINFO: Prayer Request = details01
RequestCategory = CAT01 ID is 1
2022-03-24 12:52:35.758 D/DBINFO: Photo = photo1 ID = 1
2022-03-24 12:52:35.758 D/DBINFO: Photo = photo2 ID = 2
2022-03-24 12:52:35.758 D/DBINFO: Photo = photo3 ID = 3
2022-03-24 12:52:35.758 D/DBINFO: Note = Note2 ID = 2
2022-03-24 12:52:35.759 D/DBINFO: Note = Note3 ID = 3
There are a bunch of questions like this in StackOverflow but most of that arent about room database, so I had to ask a new question.
I have an app that uses room database and that has near 4 tables and a big relationship between those tables, so for instance when I delete a user in user list fragment, that user delete(only userName and some personal info) but the user's TRANSACTIONS and LOANS hadn't been deleted.
Someone told me I have to use Cascade delete but I didn't find much info about it.
My User class model:
#Entity(tableName = "user_info")
data class UserInfo(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "user_id")
var userId: Long =0L,
#ColumnInfo(name = "full_name")
var fullName:String?,
#ColumnInfo(name= "account_id")
var accountId: String?,
#ColumnInfo(name = "mobile_number")
var mobileNumber:String?,
#ColumnInfo(name = "phone_number")
var phoneNumber:String?,
#ColumnInfo(name = "date_of_creation")
var dateOfCreation:String?,
#ColumnInfo(name = "address")
var address:String?,
)
Transactions model class:
#Entity(tableName = "transactions")
data class Transactions(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "trans_id")
var transId: Long = 0L,
#ColumnInfo(name = "user_id")
var userId: Long?,
#ColumnInfo(name = "create_date")
var createDate: String?,
#ColumnInfo(name = "bank_id")
var bankId: Long?,
#ColumnInfo(name = "description")
var description: String?,
#ColumnInfo(name = "increase")
var increase: String?,
#ColumnInfo(name = "decrease")
var decrease: String?,
#ColumnInfo(name = "loan_number")
var loanNumber: String?,
#ColumnInfo(name = "total")
var total: Long?,
#ColumnInfo(name = "type")
var type: String?
)
User DAO:
#Insert
suspend fun insert(ui: UserInfo): Long
#Update
suspend fun update(ui: UserInfo)
#Insert
suspend fun insertList(ui: MutableList<UserInfo>)
#Delete
suspend fun deleteUser(ui: UserInfo)
#Query("DELETE FROM user_info")
fun deleteAllUser()
#Query("SELECT user_info.user_id, user_info.full_name, transactions.total From user_info JOIN transactions ")
fun joinTable(): LiveData<List<UserAndMoney>>?
#Query("SELECT * from user_info WHERE user_id = :key")
fun get(key: Long): LiveData<UserInfo>?
#Query("SELECT * FROM user_info ORDER BY full_name DESC")
fun getAllUserInfo(): LiveData<List<UserInfo>>
#Query("SELECT * FROM user_info where full_name like '%' || :fullName || '%' ORDER BY full_name ASC")
fun searchUserName(fullName: String): LiveData<List<UserInfo>>
If It was not clear for you till now, let me makes it easy for you:
I need cascade delete that delets every thing about user and a record.
CASCADE is an option of a Foreign Key constraint. So you would need to define Foreign Key constraints. You define Foreign Key constraints in Room via the #Entity annotation.
As an example, as it would appear that a Transactions is related to a UserInfo via var userId: Long?, (column name user_id) you could have :-
#Entity(tableName = "transactions",
foreignKeys = [
ForeignKey(
entity = UserInfo::class,
parentColumns = ["user_id"],
childColumns = ["user_id"],
onDelete = ForeignKey.CASCADE, //<<<<<
onUpdate = ForeignKey.CASCADE // Optional
)
]
)
data class Transactions(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "trans_id")
var transId: Long = 0L,
#ColumnInfo(name = "user_id", index = true) // <<<<< best to have an index on the column, not required
var userId: Long?,
#ColumnInfo(name = "create_date")
var createDate: String?,
#ColumnInfo(name = "bank_id")
var bankId: Long?,
#ColumnInfo(name = "description")
var description: String?,
#ColumnInfo(name = "increase")
var increase: String?,
#ColumnInfo(name = "decrease")
var decrease: String?,
#ColumnInfo(name = "loan_number")
var loanNumber: String?,
#ColumnInfo(name = "total")
var total: Long?,
#ColumnInfo(name = "type")
var type: String?
)
Note
The constraint enforces referential integrity, that is a transaction can not be inserted/updated if the user_id value is not a value that exists in the user_id column of the user_info table.
The CASCADE onUpdate will cascade a change to the user_id value in the user_info table to the respective transactions.
Additional
Someone told me I have to use Cascade delete but I didn't find much info about it.
What you have been told is incorrect. You could replicate the functionality without ON DELETE CASCADE or without the Foreign Key constraint.
You could use
#Query("DELETE FROM transaction WHERE user_id=:userId")
fun cascadeDeletionsFromUser(userId: Long)
noting that if the Foreign Key constraint exists in the transactions table but didn't have an onDelete action specified, then the cascadeDeletionsFromUser function would have to be run before the user_info row is deleted. Otherwise the user_info row could not be deleted as the FK constraint would inhibit the deletion.
If you had an abstract class rather than interface then you could have:-
#Query("DELETE FROM user_info WHERE user_id=:userId")
abstract fun deleteUserById(userId: Long)
#Query("DELETE FROM transactions WHERE user_id=:userId")
abstract fun cascadeDeletionsFromUser(userId: Long)
#Transaction
#Query("")
fun deleteUserWithCascade(userId: Long) {
cascadeDeletionsFromUser(userId)
deleteUserById(userId)
}
and use the deleteUserWithCascade function to delete the transactions and user in one go.
It is more convenient to use ON DELETE CASCADE, and especially so if you have multiple depths of relationships (when it gets a little more complex ascertaining children)
I have an application that uses a Room database to store my "Friends" as accounts in an Account table
#Entity(tableName = "accounts")
data class Account(
#PrimaryKey
#ColumnInfo(name = "account_id")
val accountId: Int,
#ColumnInfo(name = "first_name", defaultValue = "")
var firstname: String,
#ColumnInfo(name = "last_name", defaultValue = "")
var lastname: String,
#ColumnInfo(name = "email", defaultValue = "")
var email: String,
#ColumnInfo(name = "status")
var status: Int,
#ColumnInfo(name = "role_id")
var roleId: Int,
#ColumnInfo(name = "lang", defaultValue = "")
var lang: String
) : Serializable
So when i refresh my accounts, there might be accounts that
will be deleted
will be inserted
will be updated
What is the most optimal way to identify what records need what action and how can i do it?
Let's say you have new accounts:
val newList: List<Account> = ... //
You can place in Dao next methods:
// To delete all accounts that are not included in new list
#Query("DELETE FROM account WHERE accountId NOT IN (: newIds)")
suspend fun deleteOutdatedAccounts(newIds: List<Int>)
// To insert/update all accounts from the new list
// Thanks to OnConflictStrategy.REPLACE strategy you get both insert and update
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUpdateAccounts(accounts: List<Account>)
// To do two methods above in single transaction
#Transaction
suspend fun refreshAccounts(newList List<Account>){
deleteOutdatedAccounts(newList.map{it.accountId})
insertUpdateAccounts(newList)
}
Afterwards you can call method refreshAccounts(newList) from your repository or ViewModel/Presenter.
I was trying to create a database view in android but getting this error:
error: There is a problem with the query: [SQLITE_ERROR] SQL error or
missing database (no such table: ChatsView)
public abstract androidx.lifecycle.LiveData>
getMessageViewLIst();
Please help me with this that how can i get data from a DatabaseView in room persistence android
DatabaseView Class
#DatabaseView("SELECT chats.username as username," +
"(SELECT chat.message FROM ChatEntityModel as chat WHERE chat.username=chats.username ORDER BY id DESC LIMIT 1) as lastMsg" +
"(SELECT chat.timeStamp FROM ChatEntityModel as chat WHERE chat.username=chats.username ORDER BY id DESC LIMIT 1) as lastMsgTime " +
"(SELECT count(1) FROM ChatEntityModel as chat WHERE chat.username=chats.username and status=4 ) as totalNewMsgCount From ChatEntityModel as chats",viewName = "ChatsView")
data class ChatsView (
val username:String,
val lastMsg:String,
val lastMsgTime:String,
val totalNewMsgCount:String
)
Entity Class
#Entity(tableName = "ChatEntityModel")
data class ChatEntityModel (
#ColumnInfo(name = "message") var KEY_MESSAGE: String,
#ColumnInfo(name = "username") var KEY_USERNAME : String,
#ColumnInfo(name = "msgsend") var KEY_MSG_SEND : Boolean,
#ColumnInfo(name = "timeStamp") var KEY_TIMESTAMP :String,
#ColumnInfo(name = "status") var KEY_STATUS :Int,
#ColumnInfo(name = "msgid") var KEY_MSG_ID :String,
#ColumnInfo(name = "timestamp_delivered") var KEY_TIME_DELIVERED :String,
#ColumnInfo(name = "timestamp_read") var KEY_TIME_READ :String,
#ColumnInfo(name = "progress") var KEY_PROGRESS :String,
#ColumnInfo(name = "type") var KEY_TYPE : String
){
#PrimaryKey(autoGenerate = true)#ColumnInfo(name = "id")var KEY_ID:Int=0
}
DaoAccess class
#Database(entities = [(ChatEntityModel::class)], views = [(ChatsView::class)], version = 1,
exportSchema = false)
abstract class DaoDatabaseAccess:RoomDatabase() {
abstract fun ChattingDao():ChattingDao
// abstract fun ChattViewDao():ChattViewDao
}
Dao
#Dao
interface ChattingDao {
#Insert(onConflict = OnConflictStrategy.ABORT)
fun insertChatList(chats: List<ChatEntityModel>)
#Insert(onConflict = OnConflictStrategy.ABORT)
fun inserChat(chats: ChatEntityModel)
#Update
fun updateMovie(chats: ChatEntityModel)
#Query("SELECT * FROM ChatEntityModel WHERE id = :id_")
fun getMovie(id_: Int): ChatEntityModel
#Query("SELECT * FROM ChatEntityModel WHERE username=:Jid ORDER BY id ASC")
fun getChatList(Jid:String?): LiveData<List<ChatEntityModel>>
#Query("Update ChatEntityModel SET status=2 , timestamp_delivered=:timeDelivered WHERE msgid=:msg_id
and status <> 3 and username=:Jid ")
fun setChatDelivered(timeDelivered: String?,msg_id:String?,Jid: String?)
#Query("SELECT * FROM ChatEntityModel WHERE status=0 ORDER BY id LIMIT 1" )
fun getUnsentMessage(): List<ChatEntityModel>
#Query("SELECT msgid FROM ChatEntityModel WHERE status=4 and username=:username ORDER BY id" )
fun getUnReadMessage(username:String): List<String>
#Query("UPDATE ChatEntityModel SET status=1 WHERE msgid= :msgId and status=0")
fun setMessageSent(msgId: String?)
#Query("SELECT * FROM ChatEntityModel WHERE msgid =:msgId")
fun checkIfExists(msgId:String?): List<ChatEntityModel>
#Query("Update ChatEntityModel SET status=3 , timestamp_read=:currentTimeMillis WHERE msgid in (:receiptId) and username=:Jid ")
fun setChatRead(currentTimeMillis: String?, receiptId:List<String>,Jid: String?)
#Query("SELECT * FROM ChatEntityModel WHERE status=4 and username=:Jid ")
fun getUnReadChats(Jid: String?):LiveData<List<ChatEntityModel>>
#Query("UPDATE ChatEntityModel set status=5 WHERE status=4 and msgid in (:chat) ")
fun setChatReceivedRead(chat:List<String>)
#Query("SELECT * FROM ChatsView")
fun getMessageViewLIst(): LiveData<List<ChatsView>>
}
Maybe it's not related to your problem, but maybe it is, sometimes errors are weird. The request of your View as several SELECT which isn't correct, maybe try fixing your Query.
EDIT: For more clarity, the issue is that the Query was wrong and was missing comas.
You need to add the database name to the abstract RoomDatabase class
#Database(entities = [ChatsView::class], version = 1)
abstract class DaoDatabaseAccess:RoomDatabase() {
}