我正在使用node-oracledb连接到Oracle数据库。该API提供了自己的承诺,可以将其转换为Promise<T>
,因此可以“转换”为Observable<T>
。
我想使用Observables
使用传统的阻止程序方式,就像这样:
try
{
connection = Oracle.getConnection(...);
resultSet = connection.execute("SELECT ... FROM ...");
}
catch (Exception)
{
resultSet = EMPTY_RESULT;
}
finally
{
if (connection)
connection.close();
}
我尝试使用Observables编写代码的过程导致了很多代码和回调。
受保护的方法getConnection()仍然非常简单:
import * as Oracle from "oracledb";
protected getConnection() : Observable<IConnection>
{
return OraUtil.from(Oracle.getConnection(this.parameters));
}
closeConnection()
方法也是如此。我直接在此处使用了promise,以避免更多代码。
protected closeConnection(subscriber : Subscriber<IExecuteReturn>, connection : IConnection) : void
{
connection.close()
.then(() => subscriber.complete())
.catch((error) => subscriber.error());
}
但是,execute()
方法是麻烦开始的地方。
protected _execute(connection : IConnection, statement : string) : Observable<IExecuteReturn>
{
return new Observable<IExecuteReturn>(
(subscriber) => {
OraUtil.from(connection.execute(statement)).subscribe(
(result) => subscriber.next(result),
(error) => {
subscriber.error(error);
this.closeConnection(subscriber, connection);
},
() => {
this.closeConnection(subscriber, connection);
});
});
}
public execute(statement : string) : Observable<IExecuteReturn>
{
return this.getConnection().pipe(
flatMap((connection) => this._execute(connection, statement))
);
}
答案 0 :(得分:1)
通常来说,在使用RxJs 6.x时,沿着这些行实现了“ connect + execute + close”序列
let connection;
Oracle.getConnection(....)
.pipe(
switchMap(conn => {
connection = conn;
return connection.execute(statement);
}),
finalize(() => connection.close())
)
.subscribe(
result => resultSet = result,
error => {
console.error(error);
}
)
语法细节可能有所不同,但是关键思想是,一旦观察到的连接发出,您就switchMap
执行该语句。订阅时会激活整个链。在订阅中,如果发生错误或执行返回的Observable完成时,请关闭连接。
答案 1 :(得分:1)
这是我通常处理连接管理的方式。核心是using
可观察的创建者,该接受者将资源工厂作为第一个参数,并将设置函数作为第二个参数。
using(() => { unsubscribe() }, resource => observableOf(resource))
resource
是具有unsubscribe
方法的对象,它作为取消订阅的一部分被调用-因此您可以在其中隐藏任何逻辑,并将任意对象的生命周期有效地绑定到可观察对象的生命周期。
我希望下面的代码有意义。
import * as Oracle from "oracledb";
import { mergeMap , ignoreElements} from 'rxjs/operators';
import { using } from 'rxjs/observable/using';
import { from as observableFrom } from 'rxjs/observable/from';
import { concat } from 'rxjs/observable/concat';
import { defer } from 'rxjs/observable/defer';
import { empty as observableEmpty } from 'rxjs/observable/empty';
class OracleConnection {
constructor(parameters) {
this.isClosed = false;
this.connection = null;
this.parameters = parameters;
}
setup() {
return defer(() => Oracle.getConnection(this.parameters)
.then(connection => { // do this in promise in case observable gets unsubscribed before connection is established
this.connection = connection;
if (this.isClosed) { // close in case connection got already closed before even got established
this.terminate();
}
return connection;
}));
}
close() {
this.isClosed = true;
if (this.connection !== null) {
const connection = this.connection;
this.connection = null;
return observableFrom(connection.close())
.pipe(ignoreElements()) // only propagate errors
}
return observableEmpty(); // connection already closed
}
terminate() {
this.close().subscribe(/* handle error from connection close */);
}
unsubscribe() { // this will get called on observable unsubscribe
if (!this.isClosed) {
this.terminate();
}
}
}
class ConnectionManager {
constructor(params) {
this.params = params;
}
getConnection() {
return using(() => new OracleConnection(this.params), oracleConnection => oracleConnection.setup())
}
}
const manager = new ConnectionManager({ /* some params */ });
manager.getConnection()
.pipe(
mergeMap(connection => concat(
connection.execute('SELECT 1'),
connection.close() // explicitly close connection
)),
// alternatively
// take(1) // to close connection automatically
);
例如,您可以做的很酷的事情是在失败的情况下轻松重试连接:
oracle.getConnection()
.pipe(
retry(3)
...
);