Android Room transactions across DAOs
Asked Answered
L

3

45

The official documentation states that:

It is recommended to have multiple Dao classes in your codebase depending on the tables they touch.

and that one can mark a method with the Transaction annotation like that:

 @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);
     }
 }

But what if a transaction spans multiple DAOs? Should I merge all DAOs into one just to support transactions, or there's a better way to do that?

Lactary answered 9/1, 2018 at 8:24 Comment(0)
G
38

You can use RoomDatabase.runInTransaction(...)

Something like:

database.runInTransaction(new Runnable(){
  @Override
  public void run(){
    Access all your daos here
  }
});
Grapeshot answered 10/1, 2018 at 12:38 Comment(3)
Won't there be a problem with that each single insert, update or delete statement also runs in a transaction. You'll get nested transactionsChaechaeronea
No you cannot access DAOs hereBrotherhood
@Brotherhood yes you can. You just need to have them injected to the wrapping class or added in different wayHersh
Y
6

Fact 1: Using @Transaction on a method causes the method to be overriden in the Dao_Impl generated class. This method looks like this:

  @Override
  public void makeFieldInactive(final long fieldId) {
      __db.beginTransaction();
      try {
          MyDao_Impl.super.makeFieldInactive(fieldId);
          __db.setTransactionSuccessful();
      } finally {
          __db.endTransaction();
      }
  }

Fact 2: The runInTransaction() method looks like this:

/**
 * Executes the specified {@link Runnable} in a database transaction. The transaction will be
 * marked as successful unless an exception is thrown in the {@link Runnable}.
 * <p>
 * Room will only perform at most one transaction at a time.
 *
 * @param body The piece of code to execute.
 */
@SuppressWarnings("deprecation")
public void runInTransaction(@NonNull Runnable body) {
    beginTransaction();
    try {
        body.run();
        setTransactionSuccessful();
    } finally {
        endTransaction();
    }
}

Conclusion: They both do the same thing.

More Info: I have done some testing and it appears that using either of them (or both, redundantly) will successfully cause your you Dao method to run in one transaction.

Answer: Using @Transaction on a method that makes changes in multiple Daos that access the same database is a safe way to make sure that all database operations that happen in the method occur in one transaction.

Edit: Example of accessing multipe Dao's in one method marked with @Transactoin:

@Transaction
 public void addItem(ItemEntity item) {
        item.setId(insert(item));
        ItemReportDao itemReportDao = AppDatabase.getIntance().itemReportDao();
        itemReportDao.addItemReport(item.getId());
}
Yokoyokohama answered 21/9, 2021 at 21:2 Comment(2)
Could you please provide an example of the "@Transaction on a method that makes changes in multiple Daos that access the same database"? Do you mark code outside of the "@Dao" with @Transaction and Room successfully generated the code?Tang
The "@Transaction" annotation has to be within one of the abstract classes marked "@Dao". The method marked "@Transaction" can then use multiple "@Dao" classes from within the "@Dao" class it is contained in. (interfaces marked "@Dao" must be converted to abstract classes in order to contain methods)Yokoyokohama
F
0

You can pass reference to dao like parameter of funtion.

Fulgurating answered 19/7, 2023 at 9:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.