使用rxJava的下游运算符的数据库(SQLite)事务

时间:2017-01-19 09:48:02

标签: java sqlite transactions rx-java

假设我有一些项目流:

Observable<Item> stream = ...

我想要实现的目标是什么?

  • 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

的应用程序作用域实例

我现在有一个解决方案。但也许你对更清洁的方式有什么建议?

2 个答案:

答案 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开始交易并每次检查交易是否已经启动,因为似乎只有在第一时间才能调用它。