A requirement of my application is to allow the user to progress through multiple steps, then upon completion write values to the database based off of entries in each step. Each step in the UI may contribute to the operations that will need to be written to the database. The data may be in multiple tables and pertain to different rows in those tables. If any of the database operations fail, then the entire operation should fail.
I initially considered loading all of the data into memory, manipulating it, then simply calling the update methods in every possible entity (with a conflict strategy of REPLACE), but there could be an extremely large amount of data in memory.
I figured that I would instead be able to assemble a List, where each Fragment in the display contributes one or more Completables, then execute those sequentially using Completable.concat() at the end of the UI flow. It would look something like below:
Completable one = Completable.fromAction(() -> Log.w(LOG_TAG, "(1)")).delay(1, TimeUnit.SECONDS);
Completable two = Completable.fromAction(() -> Log.w(LOG_TAG, "(2)")).delay(2, TimeUnit.SECONDS);
Completable three = Completable.fromAction(() -> Log.w(LOG_TAG, "(3)")).delay(3, TimeUnit.SECONDS);
Completable four = Completable.fromAction(() -> Log.w(LOG_TAG, "(4)")).delay(3, TimeUnit.SECONDS);
Completable.concatArray(one, two, three, four)
.doOnSubscribe(__ -> {
mRoomDatabase.beginTransaction();
})
.doOnComplete(() -> {
mRoomDatabase.setTransactionSuccessful();
})
.doFinally(() -> {
mRoomDatabase.endTransaction();
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe();
The Completables would actually be wrappers around Room DAO insert/update/delete methods. I would likely also perform a UI operation upon completion which is why I'm observing on the main thread.
When I execute this code, I get these logs:
W/MyPresenter: Begin transaction.
W/MyPresenter: (1)
W/MyPresenter: (2)
W/MyPresenter: (3)
W/MyPresenter: (4)
W/MyPresenter: Set transaction successful.
W/MyPresenter: End transaction.
W/System.err: java.lang.IllegalStateException: Cannot perform this operation because there is no current transaction.
W/System.err: at android.database.sqlite.SQLiteSession.throwIfNoTransaction(SQLiteSession.java:915)
W/System.err: at android.database.sqlite.SQLiteSession.endTransaction(SQLiteSession.java:398)
W/System.err: at android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:524)
W/System.err: at android.arch.persistence.db.framework.FrameworkSQLiteDatabase.endTransaction(FrameworkSQLiteDatabase.java:88)
W/System.err: at android.arch.persistence.room.RoomDatabase.endTransaction(RoomDatabase.java:220)
W/System.err: at ...lambda$doTest$22$MyPresenter(MyPresenter.java:490)
Why is the transaction gone by the time I reach doFinally? I also welcome any comments on the quality or feasibility of this approach as I'm quite new to RxJava and Room.