假设我有一些项目流:
Observable<Item> stream = ...
我想要实现的目标是什么?
db.beginTransaction()
db.setTransactionSuccessful
db.endTransaction
//some upstream operators
stream.doOnNext(i -> ...)
.map(i -> ...)
//somehow start transaction here
//operator inside transaction. All database changes will be reverted in case error
.doOnNext(i -> /*database ops*/)
.subscribe()
PS:db是可写SQLiteDatabase
我现在有一个解决方案。但也许你对更清洁的方式有什么建议?
答案 0 :(得分:2)
1)对于在单笔交易中处理所有项目的情况:
stream
.doOnSubscribe(d -> database.beginTransaction())
. ...
.subscribe(v -> {...},
e -> database.endTransaction(),
() -> { database.setTransactionSuccessful(); database.endTransaction(); })
2)对于每个项目有单独交易的情况:
class ItemWithTransaction {
Item item;
Connection conn; // connection associated with this item
boolean rollback;
}
stream
.map(i -> new ItemWithTransaction(i, openTransaction()))
.map(i -> i.conn.executeSql(..., i.item.prop1))
. ...
.map(i -> {
if (...) i.rollback = true; // error related to this item
return i;
})
. ...
.subscribe(i -> {
...
if (i.rollback) i.conn.rollback();
else i.conn.commit();
i.conn.close();
},
e -> rollbackAndCloseAllOpenConnections(),
() -> {...})
通常我不会采用第二种方法,因为它可能需要太多(不受控制的)并发数据库连接。
3)您最好重新构建代码,以便首先收集所有必需的信息,然后在短期交易中一次性进行数据库更新。我就是这样做的:
stream
. ... // processing
.buffer(...) // collect updates all or in batches
.subscribe(Collection<ItemUpdate> batch -> {
database.beginTransaction();
try {
... // update multiple items
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
},
e -> {...},
() -> {...});
答案 1 :(得分:1)
我创建了一个转换器来为所有流项目实现一个事务:
/** @return transformer which starts SQLite database transaction for each downstream operators,
* closes transaction in {@link Observable#doOnTerminate}. So transaction will be closed either on successful completion or error of stream.
* set previously opened SQLite database transaction to completed in {@link Observable#doOnCompleted} call */
public <T> Observable.Transformer<T, T> inTransaction() {
return observable -> observable
.doOnNext(o -> {
if (!database.inTransaction()) database.beginTransaction();
})
.doOnCompleted(() -> {
if (database.inTransaction()) database.setTransactionSuccessful();
})
.doOnTerminate(() -> {
if (database.inTransaction()) database.endTransaction();
});
并称之为:
stream
//start transaction here
.compose(inTransaction())
.doOnNext(i -> /*database ops*/)
.subscribe()
请注意,我在.doOnNext
开始交易并每次检查交易是否已经启动,因为似乎只有在第一时间才能调用它。