Status Update
Comments
da...@google.com <da...@google.com> #2
That part of the documentation refers when combining @Transaction
with an abstract function annotated with @Query
, @Insert
, etc...
When @Transaction
is used in a method that has a body (like your transactionLiveData
) then all Room does is wrap the method in a transaction, but that does not make sense when the return type is async, i.e. for transactionLiveData
Room basically generates:
fun transactionLiveData() : LiveData<...> {
return db.runInTransaction {
allLiveData();
}
}
As for the error not thrown when the return type is a Flow
that is indeed a bug, looks like we missed adding that return type to the list of async types to check for here:
ap...@google.com <ap...@google.com> #3
Branch: androidx-main
commit 5f3b474ee614e7c1426ca555a2edbf7a89aab95a
Author: Elif Bilgin <elifbilgin@google.com>
Date: Mon Jun 07 11:49:20 2021
Add missing Flow type in the list of async types used for verification in @Transaction method.
Bug: 190075899
Test: TransactionMethodProcessorTest.kt
Change-Id: I56ddd221f0fe636f6c1bd91ac812fa589fa65955
Relnote: Fixed an issue where Room would not error out when the return type of a @Transaction function was a Flow
M room/room-compiler/src/main/kotlin/androidx/room/processor/TransactionMethodProcessor.kt
A room/room-compiler/src/test/data/common/input/Flow.java
M room/room-compiler/src/test/kotlin/androidx/room/processor/TransactionMethodProcessorTest.kt
M room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
el...@google.com <el...@google.com>
ma...@justpinch.com <ma...@justpinch.com> #4
Cheers for the swift reply, and pointing out that I misinterpreted the @Transaction
docs. It now makes more sense.
Where I originally came from is that I'm looking for a way to combine data from multiple tables and expose the result as a Flow
, for which @Relation
doesn't offer enough flexibility. I'm basically looking for a way to do this:
fun playlistWithEpisodes(playlistId: Int): Flow<PlaylistWithEpisodes> {
return combine(
getPlaylist(playlistId), <-- Flow<Playlist>
getPlaylistEpisodes(playlistId), <-- Flow<Episodes>
) { playlist, episodes ->
PlaylistWithEpisodes(playlist, episodes)
}
}
Just like with @Relation
, this effectively runs two queries, so to perform the whole operation atomically, this should be run in a @Transaction
. Slapping @Transaction
on this method led me to opening this issue. Now that that's more clear, I'm left wondering: what's the recommended approach/best practice to do what I'm trying to do? Given that it cannot be guaranteed that the two queries (as flows) execute on the same thread, I'm maybe trying to solve this problem the wrong way? Or perhaps could I get away with not caring about running the queries in a transaction?
Looking for some tips/pointers... thanks!
da...@google.com <da...@google.com> #5
Ah - We are actually working on a feature to allow multimap return types, which would work for your use case, i.e. you would be able to define a single DAO method with return type Flow<Map<Playlist, List<Episodes>>
.
In the meantime one way to combine two Flow atomically is to use the coroutine withTransaction
API
Description
Component used:androidx.room:room-*
Version used: 2.3.0, 2.4.0-alpha02
Devices/Android versions reproduced on: N/A -> code-gen error (kapt)
The documentation on states:
@Transaction
But unless I'm misunderstanding what this is trying to communicate, reality seems to be different.
Given the following dao:
Although implementations are generated, errors are thrown for the
@Transaction
annotated functions returningLiveData
andFlowable
:This seems to contradict the earlier statement from the documentation on
@Transaction
? Is there another way to use@Transaction
with a deferred/async return type?What's also interesting is that the
@Transaction
annotated function returning aFlow
does not yield any errors. Isn'tFlow
also a deferred/async return type? Does this mean that Room can in fact guarantee that all queries in the method are performed on the same thread? If so, how?The generated code looks like this:
This function is no longer deferred, because the transactional operations involve blocking I/O, and there are assertions in place that will fail if this is called from the main thread. And even if we were to make this function suspendable (we would then have a suspendable function returning a Flow?🤨), it would still not be main-safe.
In conclusion, I suspect:
@Transaction
is no longer accurate?@Transaction
annotation returning aFlow
should also yield a code-gen error?