Duh! Manually supply ID to Dao in Entity class - android

My entity:
#Entity(tableName = "accounts")
data class Account(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "user_id")
val id: Int,
#ColumnInfo(name = "first_name")
val firstName: String,
#ColumnInfo(name = "last_name")
val lastName: String?,
#ColumnInfo(name = "email")
val email: String
)
I am doing this:
fun register(email: String, name: String) {
return dbRepository.createAccount(Account(firstName = name, email = email))
}
Dao:
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insertAccount(account: Account) : Long
Problem: Requires me to add an ID via parameter to the Account object, annoying. Because I have annotated that user_id is #PrimaryKey(autoGenerate = true) and shouldn't be manually supplied.

You need to set id as var and give it 0 as default value:
#Entity(tableName = "accounts")
data class Account(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "user_id")
var id: Int = 0,
#ColumnInfo(name = "first_name")
val firstName: String,
#ColumnInfo(name = "last_name")
val lastName: String?,
#ColumnInfo(name = "email")
val email: String
)
The default value is required to be able to create Account without providing the id because autoGenerate = true is not enough for the compiler to know that id is not required, and changing val to var because room in the background is going to parse that data and change that id so it must be mutable.
Note: This is room documentation for autoGenerate: if the field type is long or int (or its TypeConverter converts it to a long or int), Insert methods treat 0 as not-set while inserting the item.
If the field's type is Integer or Long (or its TypeConverter converts it to an Integer or a Long), Insert methods treat null as not-set while inserting the item.
So that's why the default value should be exactly 0 or null because room treats 0 and null as not-set and replace it with autoGenerated value.

Related

cascade delete in android room database KOTLIN

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)

Update all column values to boolean in room android?

I have database table as
#Entity
internal data class NotificationEntity(
#PrimaryKey #ColumnInfo(name = "notification_id") val notificationId: String,
val title: String,
val body: String?,
#ColumnInfo(name = "is_actionable") val isActionable: Boolean,
#ColumnInfo(name = "created_at") val createdAt: Instant,
#ColumnInfo(name = "is_read") val isRead: Boolean = false)
I want to update all the column values of isRead to true
I am using the following query but it isn't working.
#Query("UPDATE NotificationEntity SET is_read = 'true'")
suspend fun updateReadStatus()
Is it correct or not?
The boolean values are mapped to the integer internally. So you should use
#Query("UPDATE NotificationEntity SET is_read = 1")

Room database relation not applying

I can't make the relation between these entities.
with SQL query I can make relation but with room not working.
I think this a room bug but don't know why. and taking me a lot of time.
using 2.2.6 version.
#Entity
data class Movie(
#SerializedName("country")
val country: String,
#SerializedName("genres")
val genres: List<String>,
#SerializedName("id")
#PrimaryKey(autoGenerate = false)
val id: Int,
#SerializedName("images")
val images: List<String>? = null,
#SerializedName("imdb_rating")
val imdbRating: String,
#SerializedName("poster")
val poster: String,
#SerializedName("title")
val title: String,
#SerializedName("year")
val year: String
)
#Entity
data class FavoriteMovie(
#PrimaryKey(autoGenerate = false)
val id: Int ,
val movieId: Int,
val createTime:String
)
data class MovieAndFavoriteMovie(
#Embedded val movie: Movie,
#Relation(parentColumn = "id", entityColumn = "movieId")
val favoriteMovie: FavoriteMovie
)
DAO
#Query("SELECT * FROM Movie")
#Transaction
fun getAllFavoriteMovies(): Flow<List<MovieAndFavoriteMovie>>
Error log
java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter favoriteMovie
at com.movie_explorer.data.database.MovieAndFavoriteMovie.<init>(Unknown Source:7)
at com.movie_explorer.data.database.dao.FavoriteMovieDao_Impl$5.call(FavoriteMovieDao_Impl.java:166)
at com.movie_explorer.data.database.dao.FavoriteMovieDao_Impl$5.call(FavoriteMovieDao_Impl.java:112)
at androidx.room.CoroutinesRoom$Companion$createFlow$1$1.invokeSuspend(CoroutinesRoom.kt:81)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)

Roomdb - Update #Embedded object within an Entity

I faced a problem when updating values of #Embedded object within an Entity.
Here is my Entity class:
#Entity
data class ReplyData(
#PrimaryKey val id: String,
#ColumnInfo(name = "sequence") val sequence: Int?,
#Embedded(prefix = "from") val from: From? <--- I want to update this #Embedded object within entity
)
#Entity
data class From(
#ColumnInfo(name = "id") val id: String? = "",
#ColumnInfo(name = "name") val name: String? = "",
#ColumnInfo(name = "avatar") val avatar: String? = "",
#ColumnInfo(name = "kind") val kind: String? = ""
)
I want to update these 3 values in 1 shot instead of updating them one-by-one with this query below.
#Query("UPDATE replydata.from SET name = :name, avatar = :avatar, kind = :kind WHERE id = :id")
fun updateReplyData(id: String, name: String, avatar: String, kind: String)
How can I achieve that without affecting the original entity (ReplyData)?
I tried this solution, but it is not working at all:
#Query("SELECT * FROM message WHERE id = :id")
suspend fun getReplyMessage(id: String): Message
#Update(entity = From::class)
suspend fun updateReply(msg: Message)
suspend fun updatePinMessage(id: String, from: From) {
val msg = getReplyMessage(id)
msg.from?.avatar = from.avatar
msg.from?.name = from.name
msg.from?.kind= from.kind
updateReply(msg)
}
Some notes:
#Embedded just shares own fields in the parent.
For instance the data table columns are:
[id | sequence | fromid | fromname | fromavatar | fromkind ]
NB: Better to use "from_" instead "from"
you can update these fields directly in your queries.
Maybe so late but ...
You need create support class
#Entity
data class ReplyData(
#PrimaryKey val id: String,
#ColumnInfo(name = "sequence") val sequence: Int?,
#Embedded(prefix = "from") val from: FromItem <- here
)
#Parcelize
data class FromItem(val item: From)
data class From(
val id: String? = "",
val name: String? = "",
val avatar: String? = "",
val kind: String? = ""
)
and update from
#Query("Update ReplyData set fromitem = :item where id = :id")
fun update(id: Long, item: From)
P.S:
I didn't check this code, maybe it has some errors

Refresh items in Room database

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.

Categories

Resources