Room - Delete executes after I insert new values
Asked Answered
C

1

8

I'm studying Rxjava2 and I'm trying to integrate the Room Library with Rxjava2. The problem is: I have a populated table and every time I login in the app, I need to delete this table and then insert a new content in database. Separately, the delete and insert works fine, but when I try to insert new values after I delete the table content, the delete method deletes all the new values.. (some parts of the code is in kotlin and others in java)
I already tried this: RxJava2 + Room: data is not being inserted in DB after clearAllTables() call, but no success..

DAO

@Dao
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(list:List<Something>)

@Query("DELETE FROM SomethingTable")
fun delete()

@Query("SELECT * FROM SomethingTable")
fun getAll(): Flowable<List<Something>>

My class that calls the DAO (CallDao)

//insert
fun insertInDB(list: List<Something>) {
    Completable.fromAction {
        dbDAO!!.insert(list)
    }.observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .subscribe()
}

//delete
fun clean() {
    Completable.fromAction {
        dbDAO!!.delete()
    }.observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.single())
            .subscribe()
}

//search all
fun findAll(): Observable<List<Something>>? {
    return Observable.create { subscriber ->
        dbDAO!!.getAll()
                .subscribeOn(Schedulers.io())
                .subscribe {it->
                    subscriber.onNext(it)
                }
    }
}

Method that is called when I click in login button

private void clearAndInsertInDB() {
CallDao callDao= new CallDao(getActivity());
//delete all table values
callDao.clean();

Something sm = new Something("test1", "test2");
ArrayList<Something> list = new ArrayList<>();
list.add(sm);
list.add(sm);
//insert new values
callDao.insertInDB(list);

//get all new values in DB
callDao.findAll()
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe(res -> {
      //here gives me a IndexOutOfBoundsException
      Log.d("logDebug", res.get(0).getCodeExemple());
    });
}

Any corrections in my code is also welcome :) , but the main problem is that the delete method deletes all the new insert values and it should delete only the old values.

Croaker answered 16/11, 2018 at 19:49 Comment(5)
Instead of using Query use @Delete annotation to delete a single item and pass the Item object as a parameterSchematize
@deepakkumar, I want to delete all table content and not a single item..Croaker
Sorry, I misunderstood but what's your real problem dele executing after insert or delete method deletes all tge new values??Schematize
my problem is: the delete method is being called before I insert the new values, but it is executing after I insert the new values.Croaker
May be it's because of schedules but you can avoid this by waiting for the operation to complete using await method like in the Kotlin Coroutines await for async to complete.Schematize
K
31

You are making two asynchronous calls: one to delete the users and another to insert them again. However, even though you call first the callDao.clean(); method and after that you call callDao.insertInDB(list); , it is not guaranteed that the clean() operation will finish before the insertInDB() operation (because that's how asynchronous calls work).

This is what is happening:

Async calls without waiting

Instead, you should chain your async calls , in such a way that the second one gets called as soon as you know that the first one has already finished.

Chained async calls

How to achieve that using RxJava and Completable? Using the andThen operator as stated in this answer

You should modify your clean() and insertInDB() methods to return the Completables, use andThen to chain them, and then subscribe.

Simple example using RxJava and andThen()

FakeDatabase db = Room.databaseBuilder(this, FakeDatabase.class, "fake.db")
                      .fallbackToDestructiveMigration()
                      .build();

UserDao userDao = db.userDao();

User user1 = new User("Diego", "Garcia Lozano", "[email protected]");
User user2 = new User("Juan", "Perez", "[email protected]");
User user3 = new User("Pedro", "Lopez", "[email protected]");
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
users.add(user3);

Completable deleteAllCompletable = Completable.fromAction(userDao::deleteAll);
Completable insertUserCompletable = Completable.fromAction(() -> userDao.insertAll(users));

deleteAllCompletable
            .andThen(Completable.fromAction(() -> System.out.println("Delete finished")))
            .andThen(insertUserCompletable)
            .andThen(Completable.fromAction(() -> System.out.println("Insert finished")))
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.single())
            .subscribe();

Checking the Logcat after execution, you can see that the operations were executed in the proper order:

2018-11-19 16:07:02.056 10029-10047/? I/System.out: Delete finished
2018-11-19 16:07:02.060 10029-10047/? I/System.out: Insert finished

Afterwards, I checked the content of the database using the tool SQLite Browser and saw that the insert worked properly.

SqliteBrowser showing the proper data in the table

Using @Transaction in the DAO

You can get a better solution for your problem without using RxJava at all. Instead, you can define a Transaction in your DAO using the @Transaction annotation, as explained in this post. It would look something like this:

Dao

@Dao
public abstract class UserDao {

    @Transaction
    public void deleteAndCreate(List<User> users) {
        deleteAll();
        insertAll(users);
    }

    @Query("DELETE FROM User")
    public abstract void deleteAll();

    @Insert
    public abstract void insertAll(List<User> users);
}

Activity

Completable.fromAction(() -> userDao.deleteAndCreate(users))
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.single())
            .subscribe();

Checking the table

Table using @Transcation

Personally, I would do it with the @Transaction annotation.

Korns answered 16/11, 2018 at 21:54 Comment(4)
Thanks for your help! I didn't check as a correct answer, because I'm still trying to use Future and Completables like you said, and it's kind difficult :(Croaker
Please, check my updated answer. I would use the @Transaction annotation. CheersKorns
best solution is such a situation specially for me where I had 4 table inserting data to each table and rollback in case of conflict/ error.Swetiana
You could also use flatmap which does each asnyn call one by one and returns a success or not in one goCuddle

© 2022 - 2024 — McMap. All rights reserved.