Android Room database transactions

37,336

Solution 1

As pointed out on documentation for Transaction, you can do following:

 @Dao
 public abstract class ProductDao {
    @Insert
    public abstract void insert(Product product);

    @Delete
    public abstract void delete(Product product);

    @Transaction
    public void insertAndDeleteInTransaction(Product newProduct, Product oldProduct) {
         // Anything inside this method runs in a single transaction.
         insert(newProduct);
         delete(oldProduct);
     }
 }
 

Solution 2

As @CommonsWare pointed out, @Query are asynchronous , while @Insert , @Delete , @Update are synchronous.

If you want to execute multiple queries in single transaction , Room also provides a method for that as mentioned below.

roomDB.runInTransaction(new Runnable() {
        @Override
        public void run() {
            removeRows(ids);
            insertRows(ids);
        }
    });

I hope this will solve your problem.

Solution 3

For Room transactions in Kotlin you can use:

  • Interface with implemented method, like:
@Dao 
interface Dao {

    @Insert 
    fun insert(item: Item)

    @Delete 
    fun delete(item: Item)

    @Transaction
    fun replace(oldItem: Item, newItem: Item){
        delete(oldItem)
        insert(newItem)
    }

}
  • Or use open function, like:
@Dao 
abstract class Dao {

    @Insert 
    abstract fun insert(item: Item)

    @Delete 
    abstract fun delete(item: Item)

    @Transaction
    open fun replace(oldItem: Item, newItem: Item){
        delete(oldItem)
        insert(newItem)
    }

}

You'll get error: Method annotated with @Transaction must not be private, final, or abstract. without open modifier.

Solution 4

I believe when we are using DAO interfaces, still we can perform transaction using default interface methods. We need to add the annotation @JvmDefault and @Transaction and we can perform any operation inside that, which belong to single transaction.

@Dao
interface TestDao {
    @Insert
    fun insert(dataObj: DataType)

    @Update
    fun update(dataObj: DataType): Completable

    @Delete
    fun delete(dataObj: DataType): Completable

    @Query("DELETE FROM $TABLE_NAME")
    fun deleteAllData()

    @Query("SELECT * FROM $TABLE_NAME ORDER BY id DESC")
    fun getAllData(): Single<List<DataType>>

    @JvmDefault
    @Transaction
    fun singleTransaction(dataList: List<DataType>) {
        deleteAllData()
        dataList.forEach {
            insert(it)
        }
    }
}
Share:
37,336
Rajath
Author by

Rajath

Updated on July 31, 2020

Comments

  • Rajath
    Rajath almost 4 years

    With the new Room Database in Android, I have a requirement where there are two sequential operations that needs to be made:

    removeRows(ids);
    insertRows(ids);
    

    If I run this, I see (on examining the db) that there are some rows missing - I assume they are being deleted after inserting. viz. the first operation is running in parallel to the second.

    If I use a transaction block, such as this, then it's all fine - the first operation seems to complete before doing the second:

    roomDb.beginTransaction();
    removeRows(ids);
    roomDb.endTransaction();
    
    insertRows(ids);
    

    It's also fine if I give a sleep in-between instead:

    removeRows(ids);
    Thread.sleep(500);
    
    insertRows(ids);
    

    There doesn't seem to be much documentation for Room, and was wondering if I should use the transaction block like the above when I have sequential operations to be done, or is there any better way of doing it.

    EDIT: After @CommonsWare pointed out, @Query are asynchronous, while @Insert and @Delete are synchronous. In view of this, how would I get a query which deletes rows to be async:

    @Query("DELETE from table WHERE id IN(:ids)")
    int removeRows(List<Long> ids);
    

    According to the build output I get Deletion methods must either return void or return int (the number of deleted rows), if I try to wrap the return type in a Flowable.

  • johnny_crq
    johnny_crq over 6 years
    what if i am using interfaces?
  • guness
    guness over 6 years
    @johnny_crq I was using interfaces and it was not hard to switch to abstract classes. alternatively you might try this ugly trick on interface @Transaction @Query("DELETE FROM products; INSERT INTO products VALUES(x,y,z)") over a method.
  • IgorGanapolsky
    IgorGanapolsky about 6 years
    How would you test this in Espresso, if you are calling @Query right after Insert?
  • Amit Vikram Singh
    Amit Vikram Singh almost 6 years
    PCMIIW: Query are not always asynchronous. Query is asynchronous only when you are returning an observable e.g. Flowable or LiveData. Since in the question Query is used for removing element, return value is int and hence it will run synchronously.
  • FirstOne
    FirstOne almost 6 years
    For anyone wondering: runInTransaction: "[...]The transaction will be marked as successful unless an exception is thrown in the Runnable."
  • avalancha
    avalancha over 5 years
    @guness why would that trick be ugly? That is plain SQL syntax, I think it's readable and maintainable, absolutely fine.
  • Cililing
    Cililing over 4 years
    Those are two independent transactions.
  • Abdeldjalil Elaaeieida
    Abdeldjalil Elaaeieida over 4 years
    interface method with body!!
  • LukaszTaraszka
    LukaszTaraszka over 4 years
    What about the situation when there is a relation between two objects and second object is in other DAO?
  • guness
    guness over 4 years
    @LukaszTaraszka I think for this situation, there is a method on db itself. it should be like db.startTransaction(); aDao.do(); bDao.do(); db.endTransaction() or some kotlin style blocks. but you have to do it outside of a dao. but you have to be careful about async queries.
  • Karthik
    Karthik over 3 years
    insert update and delete annotations already run inside a transaction, wrapping these in another transaction doesn't add any value to it.
  • guness
    guness over 3 years
    but they are in different transactions, wrapping them makes them run in single transaction no?
  • André Luiz Reis
    André Luiz Reis over 3 years
    It gives me an error when I add @JvmDefault as you did in your example.
  • AK Ali
    AK Ali over 3 years
    Can you please elaborate what error you are getting?