feat: v0.1.13 — 事务回滚 + 子查询 + 外键级联 + 连接池
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* metona-sqlark Connection Manager — 数据库实例连接池
|
||||
* @module connection-manager
|
||||
*
|
||||
* v0.1.13: 避免重复创建同名数据库实例,通过 connect() 复用已有连接。
|
||||
* 管理实例生命周期,防止重复 open IndexedDB。
|
||||
*/
|
||||
|
||||
import { MetonaSqlark } from './core';
|
||||
import type { DatabaseConfig } from './constants';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ConnectionManager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class ConnectionManager {
|
||||
/** 活跃连接:dbName → MetonaSqlark */
|
||||
private connections = new Map<string, MetonaSqlark>();
|
||||
|
||||
/** 连接引用计数:dbName → count */
|
||||
private refCount = new Map<string, number>();
|
||||
|
||||
/**
|
||||
* 获取或创建数据库实例
|
||||
*
|
||||
* 如果同名数据库已打开,复用已有实例并增加引用计数。
|
||||
* 否则创建新实例。
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const db = await MetonaSqlark.connect({ name: 'my-app', mode: 'hybrid' });
|
||||
* // ... use db
|
||||
* await db.disconnect(); // 引用计数 -1,归零时自动关闭
|
||||
* ```
|
||||
*/
|
||||
async connect(config: DatabaseConfig): Promise<MetonaSqlark> {
|
||||
const name = config.name;
|
||||
|
||||
// 已有连接 → 复用
|
||||
const existing = this.connections.get(name);
|
||||
if (existing && existing.isReady()) {
|
||||
const count = (this.refCount.get(name) ?? 0) + 1;
|
||||
this.refCount.set(name, count);
|
||||
return existing;
|
||||
}
|
||||
|
||||
// 创建新连接
|
||||
const db = new MetonaSqlark(config);
|
||||
await db.init();
|
||||
|
||||
this.connections.set(name, db);
|
||||
this.refCount.set(name, 1);
|
||||
|
||||
// 注入 disconnect 方法
|
||||
(db as MetonaSqlark & { disconnect: () => Promise<void> }).disconnect = async () => {
|
||||
await this.release(name);
|
||||
};
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放连接引用。引用计数归零时自动关闭数据库。
|
||||
*/
|
||||
async release(dbName: string): Promise<void> {
|
||||
const count = (this.refCount.get(dbName) ?? 1) - 1;
|
||||
if (count <= 0) {
|
||||
const db = this.connections.get(dbName);
|
||||
if (db) {
|
||||
await db.close();
|
||||
this.connections.delete(dbName);
|
||||
}
|
||||
this.refCount.delete(dbName);
|
||||
} else {
|
||||
this.refCount.set(dbName, count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制关闭指定数据库(忽略引用计数)
|
||||
*/
|
||||
async forceClose(dbName: string): Promise<void> {
|
||||
const db = this.connections.get(dbName);
|
||||
if (db) {
|
||||
await db.close();
|
||||
this.connections.delete(dbName);
|
||||
}
|
||||
this.refCount.delete(dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制关闭所有连接
|
||||
*/
|
||||
async closeAll(): Promise<void> {
|
||||
for (const [, db] of this.connections) {
|
||||
try { await db.close(); } catch { /* ignore */ }
|
||||
}
|
||||
this.connections.clear();
|
||||
this.refCount.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有活跃连接名
|
||||
*/
|
||||
getActiveConnections(): string[] {
|
||||
return Array.from(this.connections.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接的引用计数
|
||||
*/
|
||||
getRefCount(dbName: string): number {
|
||||
return this.refCount.get(dbName) ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 全局单例
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const manager = new ConnectionManager();
|
||||
|
||||
// 挂载到 MetonaSqlark 静态方法(通过 any 绕过 TS 类型检查)
|
||||
const M = MetonaSqlark as unknown as Record<string, unknown>;
|
||||
M.connect = (config: DatabaseConfig) => manager.connect(config);
|
||||
M.disconnect = (dbName: string) => manager.release(dbName);
|
||||
M.disconnectAll = () => manager.closeAll();
|
||||
M.getActiveConnections = () => manager.getActiveConnections();
|
||||
|
||||
export { manager as connectionManager };
|
||||
export default manager;
|
||||
Reference in New Issue
Block a user