我对RxJava有问题。
我有一个getAll()
方法,它返回一个列表。它可以提供来自Room数据库的数据。
@Query("SELECT * from decks ORDER BY id ASC")
fun getAll(): Flowable<List<DeckEntity>>
DeckEntity
有一个id
和一个name
字段。
我创建了另一个名为PrepareItem
的类,因为我想用更多参数将其装箱。 (它将是Adapter
模型)进行检查:
data class PrepareItem (
var deckEntity: DeckEntity,
var countsOfCards: Int
)
因此,我想调用getAll()
,并将其映射到PrepareItem。它正在工作。
deckRepository.getAll()
.map {
it.map {
PrepareItem(it,0)
}
}
但是,有countsOfCards is equal 0
。我想进行另一个存储库调用,以获取值并进行设置。重要!每个值都需要对存储库的单个调用。因此,如果我有5件物品,那么我需要等到5件其他电话结束为止。
我尝试过,但感到困惑。 (代码已更新)
fun mapper(item: DeckEntity) : Single<PrepareItem> {
return cardRepository.getDueDatedCardsFromDeck(deckId = item.id!! /*TODO !!*/)
.map {
PrepareItem(item, it.size)
}
}
val call = deckRepository.getAll()
.flatMapIterable { item->item }
.flatMapSingle {
mapper(it)
}.toList()
.toObservable()
...
从未调用过onError
或onComplete
。为什么?
任何人都知道如何做到这一点?我希望它远离存储库。 谢谢!
更新:
解决方案:
创建一个新的班级
class DeckWithCards {
@Embedded
lateinit var deckEntity: DeckEntity
@Relation(
entity = CardEntity::class,
entityColumn = "deckId",
parentColumn = "id")
lateinit var cards: List<CardEntity>
}
为DeckDao
@Query("SELECT * from decks ORDER BY id ASC")
fun getAllWithCards(): Flowable<List<DeckWithCards>>
就可以了!谢谢你的回答。这对我有很大帮助!
答案 0 :(得分:1)
您应该考虑将硬东西扔到数据库上。
请考虑以下实体:
CardEntity
,其中包含有关特定卡的信息。它还可以确保在单副牌中可以以给定名称存在一张卡。
@Entity(
tableName = "cards",
indices = {
@Index(value = { "name", "deck_id" }, unique = true)
}
)
public class CardEntity {
//region Column Definitions
@PrimaryKey
@ColumnInfo(name = "id")
private Long id;
@NonNull
@ColumnInfo(name = "name")
private String name = "";
@NonNull
@ColumnInfo(name = "deck_id")
private Long deckId = 0L;
//endregion
//region Getters and Setters
(...)
//endregion
}
DeckEntity
,其中包含有关特定卡座的信息。
@Entity(tableName = "decks")
public class DeckEntity {
//region Column Definitions
@PrimaryKey
@ColumnInfo(name = "id")
private Long id;
@NonNull
@ColumnInfo(name = "name")
private String name = "";
//endregion
//region Getters and Setters
(...)
//endregion
}
DeckWithCardsView
是一个投影,它将DeckEntity
,CardEntity
的集合和一些元数据(在这种情况下-该卡片组中的许多纸牌)组合在一起。
public class DeckWithCardsView {
//region Deck
@Embedded
private DeckEntity deck;
public DeckEntity getDeck() {
return deck;
}
public void setDeck(DeckEntity deck) {
this.deck = deck;
}
//endregion
//region Cards
@Relation(
entity = CardEntity.class,
entityColumn = "deck_id",
parentColumn = "id")
private List<CardEntity> cards = new ArrayList<>();
public List<CardEntity> getCards() {
return cards;
}
public void setCards(List<CardEntity> cards) {
this.cards = cards;
}
//endregion
//region Cards count
private Integer count = 0;
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
//endregion
}
完整的Dao
代码如下所示:
@Dao
public abstract class DeckDao {
//region Deck
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void insertDeck(DeckEntity entity);
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void insertDecks(List<DeckEntity> entities);
//endregion
//region Card
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void insertCard(CardEntity entity);
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void insertCards(List<CardEntity> cards);
//endregion
//region Deck with Cards
@Transaction
@Query(
"SELECT D.id as id, D.name as name, count(C.id) as count "
+ "FROM decks D "
+ "INNER JOIN cards C "
+ "WHERE C.deck_id = D.id "
+ "GROUP BY deck_id")
public abstract Flowable<List<DeckWithCardsView>> getAll();
//endregion
}
它实现了以下功能:
最重要的部分是在Sqlite查询代码中。一行一行:
选择D.id作为ID,D.name作为名称,count(C.id)作为count
它定义了我们期望查询从哪些确切列中返回数据库的值。特别是:
D.id as id
-它将从别名为id
(甲板)的列中返回D
,D.name as name
-它将从别名为name
(甲板)的列中返回D
,count(C.id) as count
-它会返回从别名为C
(卡)的列中检索到的许多ID。从甲板D内联接卡C在哪里C.deck_id = D.id
它定义了我们想要从别名为decks
的表D
和别名为cards
的{{1}}的表中检索值,并定义了C
上的关系CardEntity
至deck_id
的{{1}}列中。
GROUP BY deck_id
由于进行分组,我们可以轻松地DeckEntity
来检索为每个卡组返回的卡数量。
由于我们还在id
中使用了count(C.id)
注释-对应的(别名)列将与嵌入的@Embedded
平台字段中的正确字段匹配。
数据库管理器
已经准备好了这样的实体,投影和dao-数据库管理器可以很简单地实现:
DeckWithCardsView
示例数据:
我还准备了一个示例代码,该代码创建了五个完整的牌组(每个牌组中所有西装和等级)。
DeckEntity
它创建ID和牌组名称的列表,并创建此类表的产品。平面映射此类产品会产生五个甲板实体的完整列表。
public Completable saveAllDecks(@Nullable List<DeckEntity> decks) {
return Completable.fromAction(
() -> database
.deckDao()
.insertDecks(decks)
).subscribeOn(Schedulers.io());
}
public Completable saveAllCards(@Nullable List<CardEntity> cards) {
return Completable.fromAction(
() -> database
.deckDao()
.insertCards(cards)
).subscribeOn(Schedulers.io());
}
public Flowable<List<DeckWithCardsView>> getDecksWithCards() {
return database
.deckDao()
.getAll()
.subscribeOn(Schedulers.io());
}
使用类似的方法,我为以前准备的卡组创建了一套完整的卡。
最后的修饰:
艰苦的工作已经完成。剩下的简单部分:
private static final Long[] DECK_IDS = {
1L, 2L, 3L, 4L, 5L
};
private static final String[] DECK_NAMES = {
"Deck 1", "Deck 2", "Deck 3", "Deck 4", "Deck 5"
};
Completable prepareDecks() {
final Observable<Long> ids = Observable.fromArray(DECK_IDS);
final Observable<String> names = Observable.fromArray(DECK_NAMES);
final List<DeckEntity> decks = ids.flatMap(id -> names.map(name -> {
final DeckEntity entity = new DeckEntity();
entity.setId(id);
entity.setName(name);
return entity;
})).toList().blockingGet();
return cinemaManager.saveDecks(decks);
}
结果:
因此,我们有两个表。用于卡片组和卡片。订阅private static final String[] CARD_SUITS = {
"diamonds", "hearts", "spades", "clubs"
};
private static final String[] CARD_RANKS = {
"Two of ",
"Three of ",
"Four of ",
"Five of ",
"Six of ",
"Seven of",
"Eight of ",
"Nine of ",
"Ten of ",
"Jack of ",
"Queen of ",
"King of ",
"Ace of "
};
Completable prepareCards() {
final Observable<Long> decks = Observable.fromArray(DECK_IDS);
final Observable<String> suits = Observable.fromArray(CARD_SUITS);
final Observable<String> ranks = Observable.fromArray(CARD_RANKS);
final List<CardEntity> cards = decks.flatMap(deck -> suits.flatMap(suit -> ranks.map(rank -> {
final CardEntity entity = new CardEntity();
entity.setName(String.format("%s %s", rank, suit));
entity.setDeckId(deck);
return entity;
}))).toList().blockingGet();
return cinemaManager.saveCards(cards);
}
可以访问一组牌组。具有完整的卡片收集和卡片数目(由数据库计算)。
请记住-由于已完成与带卡牌桌的关系,因此不需要使用
private Completable prepareData() { return prepareDecks().andThen(prepareCards()); } void observeDecksWithCards() { disposables.add( prepareData() .andThen(deckManager.getDecksWithCards()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( this::handleObserveDecksWithCardsSuccess, this::handleObserveDecksWithCardsError)); } private void handleObserveDecksWithCardsSuccess(@NonNull List<DeckWithCardsView> decks) { Timber.v("Received number of decks: %d", decks.size()); } private void handleObserveDecksWithCardsError(@NonNull Throwable throwable) { Timber.e(throwable.getLocalizedMessage()); }
sqlite方法。您可以只在getDecksWithCards
投影中使用count
。这将大大简化代码。