Update Query not updating entity values Room - android

Room , Mvvm Created , Kotlin - Add , Delete are working
but when i run update nothing changes
i had debugged the app , dao , repo and viewModel class are showing the changes but no changes reflect on my recyclerView or when i destroy and again open my app it does not changes
My Model/Entity Classs :
data class IceBreakerModel(val question:String,
val date:String,
val option1:String,
val option2:String,
val option3:String){
#PrimaryKey(autoGenerate = true)
var id: Int = 0
}
Dao : -
interface IcebreakerDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertQuestion(question:IceBreakerModel)
#Query("DELETE FROM ice_breaker_questions WHERE id=:ID")
fun deleteQuestion(ID:Int)
#Query("SELECT * FROM ice_breaker_questions")
fun getAllIcebreakerQuestions():LiveData<List<IceBreakerModel>>
#Update(onConflict = OnConflictStrategy.REPLACE)
fun updateIcebreakerQuestion(question:IceBreakerModel)
// #Query("UPDATE ice_breaker_questions SET question =:newQuestion ,date=:newDate,option1=:newOption1,option2=:newOption2,option3=:newOption3 WHERE id=:ID")
// fun updateQuestion(newQuestion:String,newDate:String,newOption1:String,newOption2:String,newOption3:String,ID:Int)
}
Repo :-
private class UpdateQuestion(dao:IcebreakerDao):AsyncTask<IceBreakerModel,Unit,Unit>(){
val questionDao = dao
override fun doInBackground(vararg params: IceBreakerModel?) {
val model = params[0]!!
questionDao.updateIcebreakerQuestion(model)
// questionDao.updateQuestion(model.question,model.date,model.option1,model.option2,model.option3,model.id)
}
}
and Finally Main Activity where i am updating
iceBreakerViewModel.updateQuestion(IceBreakerModel(question,Date().toString(),option1,option2,option3))
adapter.notifyDataSetChanged()

The method you commented:
#Query("UPDATE ice_breaker_questions SET question =:newQuestion ,date=:newDate,option1=:newOption1,option2=:newOption2,option3=:newOption3 WHERE id=:ID")
fun updateQuestion(newQuestion:String,newDate:String,newOption1:String,newOption2:String,newOption3:String,ID:Int)
Is the way to go. At least it's how I'm used to update my columns.
Also, I think you need to adapter.notifyDataSetChanged() inside the observer of the ViewModel in your activity and in your ViewModel, assuming you are using LiveData.
Let me know if you need anything else, as I might missed the point somewhere

Related

Query method parameters should either be a type that can be converted into a database column or a List / Array that contains such type. Why?

#Dao
interface LectureDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun upsert(lecture: Lecture)
#Delete
fun delete(lecture: Lecture)
#Query("update lecture set attendanceCount = attendanceCount + 1")
fun incrementAttendance(lecture: Lecture)
#Query("select * from lecture")
fun getLectures(): LiveData<List<Lecture>>
}
Well, I have a problem when I write incrementAttendance function I got this error. What I'm trying to do is I want to increase the absentee status by 1 for a specific lesson. Here is my model:
#Entity(tableName = "lecture")
data class Lecture(
#PrimaryKey(autoGenerate = true)
val id: Int?,
val lectureName: String?,
val attendanceCount: Int?
)
Why do I need TypeConverter for this? And how can I solve?
And I already tried to change kotlin-room versions. And that didn't help.
I solved the problem by giving direct id instead of lecture class to parameter.
#Query("update lecture set absenceCount = absenceCount + 1 where id = :id")
suspend fun incrementAbsenceCount(id: Int)

DIsplay LiveData in a ListView

I have an Activity where I have a LiveData and I wanna display it in a ListView.
My code snippet:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_records)
this.recordListView.emptyView = recordListEmptyView
viewModel = ViewModelProvider(this).get(RecordsViewModel::class.java)
recordDAO = AppDatabase.getDb(this).recordDao()
recordDAO.findAllSync().observe(this) {
//display livedata in listview after getting added
//adapter = ArrayAdapter(this,list_items,syncedRecords)
//listview.adapter = adapter
}
}
After I added a record I wanna display the records in the place where I have the comment.
I could make it work with a normal findAll() and set my adapter to the return of findAll() but I could not make it work with LiveData.
My DAO class:
#Dao
interface RecordDAO{
#Update(onConflict = REPLACE)
fun update(record:Record) : Int
#Insert(onConflict = IGNORE)
fun persist(record: Record): Long
#Delete
fun delete(record: Record): Int
#Delete
fun deleteAll(records: List<Record>)
#Query("SELECT * FROM record")
fun findAll(): List<Record>
#Query("SELECT * FROM record")
fun findAllSync(): LiveData<List<Record>>
#Query("SELECT * FROM record WHERE id = :id")
fun findById(id: Int): Record?
}
Here is how it should work. Since you did not inclue the viewmodel code, it is difficult to know the exact error. Here you observe the result of a function (recordDAO.findAllSync().observe), so I guess that the livedata is a variable only in the scope of findAllSync.
This is not how it works. Live data should be a member variable of the viewModel
class RecordsViewModel : ViewModel() {
val records = MutableLiveData<RecordDAO>()
// this function gives the records to the live data. No need for a return value
fun findAllSync() {
// Retrieve the records somehow, here this should be edited by you
records.value = someService.getRecords()
}
}
Then in the activity/fragment, you observe the live data and request a record update (You can also request the update directly from the init of the viewmodel depending on how/when you want to get the data)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_records)
this.recordListView.emptyView = recordListEmptyView
viewModel = ViewModelProvider(this).get(RecordsViewModel::class.java)
// THIS CHANGED
viewModel.findAllSync()
viewModel.records.observe(this) { records ->
val adapter = ArrayAdapter(this,list_items, records)
listview.adapter = adapter
}
}
Note: I used a MutableLiveData. The cleaner way is to have a private MutableLiveData and a public LiveData
private val _records = MutableLiveData<RecordDAO>()
val records: LiveData<RecordDAO> = _records
The vm uses _records.value to set the data but since the Activity can only read the non mutable one, it cannot edit the value, only the viewmodel can. You don't need to do it this way it but it's always nice to know

Using the repository functions in the viewModel with coruntineScope in android Room

Working with Androind and Room for the first time, and i was able to follow a few codelabs and tutorials to achieve inserting and showing a list of my entities, but i cant seem to be able to use my other Repository methods in my ViewModel due to a type mismatch, here is my ViewModel file
class CustomerViewModel(application: Application) : AndroidViewModel(application) {
// The ViewModel maintains a reference to the repository to get data.
private val repository: CustomerRepository
// LiveData gives us updated words when they change.
val allCustomers: LiveData<List<Customer>>
init {
// Gets reference to Dao from db to construct
// the correct repo.
val customerDao = AppDatabase.getInstance(application).customerDao()
repository = CustomerRepository(customerDao)
allCustomers = repository.getCustomers()
}
fun insert(customer: Customer) = viewModelScope.launch {
repository.insert(customer)
}
}
and im trying to add a method like
fun find(id: Int) = viewModelScope.launch {
return repository.getCustomerByLocalId(id)
}
but the ide says there's a type mismatch here? Required: Customer, Found: Job
here is my repository:
class CustomerRepository(private val customerDao: CustomerDao) {
fun getCustomers(): LiveData<List<Customer>> = customerDao.getAlphabetizedCustomers()
suspend fun getCustomerByLocalId(local_Id: Int): Customer =
customerDao.customerByLocalId(local_Id)
suspend fun insert(customer: Customer) {
customerDao.insert(customer)
}
companion object {
// For Singleton instantiation
#Volatile
private var instance: CustomerRepository? = null
fun getInstance(customerDao: CustomerDao) =
instance ?: synchronized(this) {
instance ?: CustomerRepository(customerDao).also { instance = it }
}
}
}
methods in CustomerDao
#Query("SELECT * from customers ORDER BY name ASC")
fun getAlphabetizedCustomers(): LiveData<List<Customer>>
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(customer: Customer)
#Query("SELECT * FROM customers WHERE local_id = :localId")
suspend fun customerByLocalId(localId: Int): Customer
EDIT
I tried #lena-bru 's suggestion but the error is still there, there appears to be 2 different ones, the type mismatch and that there should not be a return. are you supposed to create this method in a different location?
The IDE error
change this:
fun find(id: Int) = viewModelScope.launch {
return repository.getCustomerByLocalId(id)
}
to this:
fun find(id: Int): Customer = viewModelScope.launch {
withContext(Dispatchers.IO){
repository.getCustomerByLocalId(id)
}
}
Your find method as defined above is void, it needs to return type Customer
Also you need to provide a context, and remove the return keyword

How do I persist changes in RecyclerView order with Room and LiveData?

I'm having trouble figuring out how I'm supposed to update data in a Room Database after a change in RecyclerView item order. How am I supposed to update LiveData items in Room based on user action?
Using ItemTouchHelper.Callback I've set up an onMove callback that can make changes to the order of items presented to the user (on drag and drop), but when I make a call to update the order of items in the Room Database, using a ViewModel object, the user can then only move items one at a time. So if you drag an item, it will only move one space.
This is the onMove function I have defined in the ListAdapter, which implements ItemTouchHelperAdapter for the callback.
override fun onMove(
recyclerView: RecyclerView,
fromViewHolder: RecyclerView.ViewHolder,
toViewHolder: RecyclerView.ViewHolder
): Boolean {
d(this.TAG, "swap viewHolders: " + fromViewHolder.adapterPosition + " to " + toViewHolder.adapterPosition)
val workoutRoutine1 = workoutRoutines[fromViewHolder.adapterPosition]
val workoutRoutine2 = workoutRoutines[toViewHolder.adapterPosition]
workoutRoutine1.orderNumber = toViewHolder.adapterPosition.toLong()
workoutRoutine2.orderNumber = fromViewHolder.adapterPosition.toLong()
//this.workoutRoutinesViewModel.update(workoutRoutine1)
//this.workoutRoutinesViewModel.update(workoutRoutine2)
notifyItemMoved(fromViewHolder.adapterPosition, toViewHolder.adapterPosition)
return true
}
This is my DAO object
#Dao
interface WorkoutRoutineDAO {
#Insert
suspend fun insert(workoutRoutine: WorkoutRoutine)
#Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun update(workoutRoutine: WorkoutRoutine)
#Delete
suspend fun delete(workoutRoutine: WorkoutRoutine)
#Query("DELETE FROM workout_routine_table")
fun deleteAll()
#Query("SELECT * FROM workout_routine_table ORDER BY order_number ASC")
fun getAllWorkoutRoutines(): LiveData<List<WorkoutRoutine>>
#Query("SELECT COALESCE(MAX(order_number), -1) FROM workout_routine_table")
fun getLargestOrderNumber(): Long
}
This is my RoomDatabase object
#Database(entities = [WorkoutRoutine::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun workoutRoutineDAO(): WorkoutRoutineDAO
companion object {
#Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(
context: Context,
scope: CoroutineScope
): AppDatabase {
return INSTANCE ?: synchronized(this) {
val instance =
Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
.addCallback(WorkoutRoutineDatabaseCallback(scope))
.build()
INSTANCE = instance
instance
}
}
}
private class WorkoutRoutineDatabaseCallback(
private val scope: CoroutineScope
) : RoomDatabase.Callback() {
}
}
This is the ViewModel object I implemented.
class WorkoutRoutinesViewModel(application: Application) : AndroidViewModel(application) {
private val workoutRoutinesRepository: WorkoutRoutineRepository
val allWorkoutRoutines: LiveData<List<WorkoutRoutine>>
init {
// Get the DAO
val workoutRoutineDAO = AppDatabase.getDatabase(application, viewModelScope).workoutRoutineDAO()
// Build a new data repository for workout routines
workoutRoutinesRepository = WorkoutRoutineRepository(workoutRoutineDAO)
// Get a live view of the workout routines database
allWorkoutRoutines = workoutRoutinesRepository.allRoutines
}
fun insert(workoutRoutine: WorkoutRoutine) = viewModelScope.launch(Dispatchers.IO) {
workoutRoutinesRepository.insert(workoutRoutine)
}
fun update(workoutRoutine: WorkoutRoutine) = viewModelScope.launch(Dispatchers.IO) {
workoutRoutinesRepository.update(workoutRoutine)
}
fun delete(workoutRoutine: WorkoutRoutine) = viewModelScope.launch(Dispatchers.IO) {
workoutRoutinesRepository.delete(workoutRoutine)
}
}
I expect the user to be able to move the item n spaces, then drop, and have the update to Room database execute when the user drops the item, but if I put the Room update in the onMove method, the user can only move the item once.
I'm trying to understand the right way to update the Room data when the order of objects change in the recycler view. I'm trying to get the order of those objects to persist even when the user exits the app or changes activities or whatever. How am I supposed to echo those changes back to the Room database, using LiveData?
You can follow this guide on Udacity. It is free, made by Google and uses Kotlin.

Android ROOM LIVEDATA not emitting the results

Hi i have a list of items that i retrieve from a data source and then i apply a map on the observable to store the data into ROOM.
It manages to add it to the table but when i try to retrieve a LiveData of it, It doesnt seem to notify my observer of the results.
There is definetly data in the table and my query works as i changed the return time from LiveData to simple a List and that worked perfectly fine
Here is my data class
#Entity
data class Item(
#PrimaryKey
val id:String,
val title: String,
val description: String,
val imgUrl: String,
val usageRules : List<String>,
)
Here is my DAO that exposes a func that add all the list of Items
#Dao
interface MyDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun saveAllItems(itemList: MutableList<Item>)
#Query("SELECT * FROM items")
fun getAllItems(): LiveData<MutableList<Item>>
}
My DB class
#Database(entities = arrayOf(Item::class), version = 1, exportSchema = false)
#TypeConverters(UsageRulesTypeConverter::class)
abstract class MyDatabase : RoomDatabase() {
abstract fun getProductDao(): MyDao
}
Below is how i insert data into the database:
#Inject
lateInt val database : MyDatabase
override fun getAllItems(): Single<LiveData<MutableList<Item>>> {
//retrieve new data using rertrofit
networkController.getItems().map { responseItems ->
saveAllItems(responseItems )
getItems()
}
#Transaction
fun saveAllItems(allItems: ItemsDataSource) {
database.getProductDao().saveAllItems(allItems.loadedItems)
database.getProductDao().saveAllItems(allItems.savedItems)
database.getProductDao().saveAllItems(allItems.expiredItems)
database.getProductDao().saveAllItems(allItems.unloadedItems)
}
fun getItems() : LiveData<MutableList<Item>>{
return database.getProductDao().getAllItems()
}
My data source retrieved 4 lists of Items that i then save all of them in one entity/table but LiveData doesnt notify my UI about it?
ViewModel:
override fun getUnloadedOffers(): LiveData<MutableList<ProductOffer>> {
if (!this::itemsLiveData.isInitialized) {
itemsLiveData= MutableLiveData()
//get data from network or database
itemDelegator.getItems()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ items->
itemsLiveData= items
})
}
return itemsLiveData
}
UI
viewModel = ViewModelProviders.of(this, viewModelFactory).get(MyViewModel::class.java)
viewModel.getItems().observe(this, Observer {
items->
items?.let {
adapter = SomeAdapter(items)
itemsRecyclerView.adapter = adapter
adapter.notifyDataSetChanged()
}
})
Does your function getUnloadedOffers() return empty mutableList? I'm not sure if passing items to itemsLiveData is going to work, since observing LiveData is running on bg thread (if I'm not mistaken)
Try to use vararg instead of List when saving collection of data.
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun saveAllItems(vararg itemList: Item)
And then you can call this method using * (also known as Spread Operator) on your list like this
saveAllItems(*responseItems.toTypedArray())

Categories

Resources