@simplysm/orm-node 13.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cache/typecheck-node.tsbuildinfo +1 -0
- package/README.md +418 -0
- package/dist/connections/mssql-db-conn.js +386 -0
- package/dist/connections/mssql-db-conn.js.map +7 -0
- package/dist/connections/mysql-db-conn.js +227 -0
- package/dist/connections/mysql-db-conn.js.map +7 -0
- package/dist/connections/postgresql-db-conn.js +191 -0
- package/dist/connections/postgresql-db-conn.js.map +7 -0
- package/dist/core-common/src/common.types.d.ts +74 -0
- package/dist/core-common/src/common.types.d.ts.map +1 -0
- package/dist/core-common/src/env.d.ts +6 -0
- package/dist/core-common/src/env.d.ts.map +1 -0
- package/dist/core-common/src/errors/argument-error.d.ts +25 -0
- package/dist/core-common/src/errors/argument-error.d.ts.map +1 -0
- package/dist/core-common/src/errors/not-implemented-error.d.ts +29 -0
- package/dist/core-common/src/errors/not-implemented-error.d.ts.map +1 -0
- package/dist/core-common/src/errors/sd-error.d.ts +27 -0
- package/dist/core-common/src/errors/sd-error.d.ts.map +1 -0
- package/dist/core-common/src/errors/timeout-error.d.ts +31 -0
- package/dist/core-common/src/errors/timeout-error.d.ts.map +1 -0
- package/dist/core-common/src/extensions/arr-ext.d.ts +15 -0
- package/dist/core-common/src/extensions/arr-ext.d.ts.map +1 -0
- package/dist/core-common/src/extensions/arr-ext.helpers.d.ts +19 -0
- package/dist/core-common/src/extensions/arr-ext.helpers.d.ts.map +1 -0
- package/dist/core-common/src/extensions/arr-ext.types.d.ts +215 -0
- package/dist/core-common/src/extensions/arr-ext.types.d.ts.map +1 -0
- package/dist/core-common/src/extensions/map-ext.d.ts +57 -0
- package/dist/core-common/src/extensions/map-ext.d.ts.map +1 -0
- package/dist/core-common/src/extensions/set-ext.d.ts +36 -0
- package/dist/core-common/src/extensions/set-ext.d.ts.map +1 -0
- package/dist/core-common/src/features/debounce-queue.d.ts +53 -0
- package/dist/core-common/src/features/debounce-queue.d.ts.map +1 -0
- package/dist/core-common/src/features/event-emitter.d.ts +66 -0
- package/dist/core-common/src/features/event-emitter.d.ts.map +1 -0
- package/dist/core-common/src/features/serial-queue.d.ts +47 -0
- package/dist/core-common/src/features/serial-queue.d.ts.map +1 -0
- package/dist/core-common/src/index.d.ts +32 -0
- package/dist/core-common/src/index.d.ts.map +1 -0
- package/dist/core-common/src/types/date-only.d.ts +152 -0
- package/dist/core-common/src/types/date-only.d.ts.map +1 -0
- package/dist/core-common/src/types/date-time.d.ts +96 -0
- package/dist/core-common/src/types/date-time.d.ts.map +1 -0
- package/dist/core-common/src/types/lazy-gc-map.d.ts +80 -0
- package/dist/core-common/src/types/lazy-gc-map.d.ts.map +1 -0
- package/dist/core-common/src/types/time.d.ts +68 -0
- package/dist/core-common/src/types/time.d.ts.map +1 -0
- package/dist/core-common/src/types/uuid.d.ts +35 -0
- package/dist/core-common/src/types/uuid.d.ts.map +1 -0
- package/dist/core-common/src/utils/bytes.d.ts +51 -0
- package/dist/core-common/src/utils/bytes.d.ts.map +1 -0
- package/dist/core-common/src/utils/date-format.d.ts +90 -0
- package/dist/core-common/src/utils/date-format.d.ts.map +1 -0
- package/dist/core-common/src/utils/json.d.ts +34 -0
- package/dist/core-common/src/utils/json.d.ts.map +1 -0
- package/dist/core-common/src/utils/num.d.ts +60 -0
- package/dist/core-common/src/utils/num.d.ts.map +1 -0
- package/dist/core-common/src/utils/obj.d.ts +258 -0
- package/dist/core-common/src/utils/obj.d.ts.map +1 -0
- package/dist/core-common/src/utils/path.d.ts +23 -0
- package/dist/core-common/src/utils/path.d.ts.map +1 -0
- package/dist/core-common/src/utils/primitive.d.ts +18 -0
- package/dist/core-common/src/utils/primitive.d.ts.map +1 -0
- package/dist/core-common/src/utils/str.d.ts +103 -0
- package/dist/core-common/src/utils/str.d.ts.map +1 -0
- package/dist/core-common/src/utils/template-strings.d.ts +84 -0
- package/dist/core-common/src/utils/template-strings.d.ts.map +1 -0
- package/dist/core-common/src/utils/transferable.d.ts +47 -0
- package/dist/core-common/src/utils/transferable.d.ts.map +1 -0
- package/dist/core-common/src/utils/wait.d.ts +19 -0
- package/dist/core-common/src/utils/wait.d.ts.map +1 -0
- package/dist/core-common/src/utils/xml.d.ts +36 -0
- package/dist/core-common/src/utils/xml.d.ts.map +1 -0
- package/dist/core-common/src/zip/sd-zip.d.ts +80 -0
- package/dist/core-common/src/zip/sd-zip.d.ts.map +1 -0
- package/dist/db-conn-factory.js +88 -0
- package/dist/db-conn-factory.js.map +7 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +7 -0
- package/dist/node-db-context-executor.js +129 -0
- package/dist/node-db-context-executor.js.map +7 -0
- package/dist/orm-common/src/db-context.d.ts +669 -0
- package/dist/orm-common/src/db-context.d.ts.map +1 -0
- package/dist/orm-common/src/errors/db-transaction-error.d.ts +51 -0
- package/dist/orm-common/src/errors/db-transaction-error.d.ts.map +1 -0
- package/dist/orm-common/src/exec/executable.d.ts +79 -0
- package/dist/orm-common/src/exec/executable.d.ts.map +1 -0
- package/dist/orm-common/src/exec/queryable.d.ts +708 -0
- package/dist/orm-common/src/exec/queryable.d.ts.map +1 -0
- package/dist/orm-common/src/exec/search-parser.d.ts +72 -0
- package/dist/orm-common/src/exec/search-parser.d.ts.map +1 -0
- package/dist/orm-common/src/expr/expr-unit.d.ts +25 -0
- package/dist/orm-common/src/expr/expr-unit.d.ts.map +1 -0
- package/dist/orm-common/src/expr/expr.d.ts +1369 -0
- package/dist/orm-common/src/expr/expr.d.ts.map +1 -0
- package/dist/orm-common/src/index.d.ts +32 -0
- package/dist/orm-common/src/index.d.ts.map +1 -0
- package/dist/orm-common/src/models/system-migration.d.ts +10 -0
- package/dist/orm-common/src/models/system-migration.d.ts.map +1 -0
- package/dist/orm-common/src/query-builder/base/expr-renderer-base.d.ts +95 -0
- package/dist/orm-common/src/query-builder/base/expr-renderer-base.d.ts.map +1 -0
- package/dist/orm-common/src/query-builder/base/query-builder-base.d.ts +66 -0
- package/dist/orm-common/src/query-builder/base/query-builder-base.d.ts.map +1 -0
- package/dist/orm-common/src/query-builder/mssql/mssql-expr-renderer.d.ts +84 -0
- package/dist/orm-common/src/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -0
- package/dist/orm-common/src/query-builder/mssql/mssql-query-builder.d.ts +45 -0
- package/dist/orm-common/src/query-builder/mssql/mssql-query-builder.d.ts.map +1 -0
- package/dist/orm-common/src/query-builder/mysql/mysql-expr-renderer.d.ts +84 -0
- package/dist/orm-common/src/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -0
- package/dist/orm-common/src/query-builder/mysql/mysql-query-builder.d.ts +54 -0
- package/dist/orm-common/src/query-builder/mysql/mysql-query-builder.d.ts.map +1 -0
- package/dist/orm-common/src/query-builder/postgresql/postgresql-expr-renderer.d.ts +84 -0
- package/dist/orm-common/src/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -0
- package/dist/orm-common/src/query-builder/postgresql/postgresql-query-builder.d.ts +52 -0
- package/dist/orm-common/src/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -0
- package/dist/orm-common/src/query-builder/query-builder.d.ts +7 -0
- package/dist/orm-common/src/query-builder/query-builder.d.ts.map +1 -0
- package/dist/orm-common/src/schema/factory/column-builder.d.ts +394 -0
- package/dist/orm-common/src/schema/factory/column-builder.d.ts.map +1 -0
- package/dist/orm-common/src/schema/factory/index-builder.d.ts +151 -0
- package/dist/orm-common/src/schema/factory/index-builder.d.ts.map +1 -0
- package/dist/orm-common/src/schema/factory/relation-builder.d.ts +337 -0
- package/dist/orm-common/src/schema/factory/relation-builder.d.ts.map +1 -0
- package/dist/orm-common/src/schema/procedure-builder.d.ts +202 -0
- package/dist/orm-common/src/schema/procedure-builder.d.ts.map +1 -0
- package/dist/orm-common/src/schema/table-builder.d.ts +259 -0
- package/dist/orm-common/src/schema/table-builder.d.ts.map +1 -0
- package/dist/orm-common/src/schema/view-builder.d.ts +183 -0
- package/dist/orm-common/src/schema/view-builder.d.ts.map +1 -0
- package/dist/orm-common/src/types/column.d.ts +172 -0
- package/dist/orm-common/src/types/column.d.ts.map +1 -0
- package/dist/orm-common/src/types/db.d.ts +175 -0
- package/dist/orm-common/src/types/db.d.ts.map +1 -0
- package/dist/orm-common/src/types/expr.d.ts +474 -0
- package/dist/orm-common/src/types/expr.d.ts.map +1 -0
- package/dist/orm-common/src/types/query-def.d.ts +351 -0
- package/dist/orm-common/src/types/query-def.d.ts.map +1 -0
- package/dist/orm-common/src/utils/result-parser.d.ts +38 -0
- package/dist/orm-common/src/utils/result-parser.d.ts.map +1 -0
- package/dist/orm-node/src/connections/mssql-db-conn.d.ts +44 -0
- package/dist/orm-node/src/connections/mssql-db-conn.d.ts.map +1 -0
- package/dist/orm-node/src/connections/mysql-db-conn.d.ts +38 -0
- package/dist/orm-node/src/connections/mysql-db-conn.d.ts.map +1 -0
- package/dist/orm-node/src/connections/postgresql-db-conn.d.ts +39 -0
- package/dist/orm-node/src/connections/postgresql-db-conn.d.ts.map +1 -0
- package/dist/orm-node/src/db-conn-factory.d.ts +25 -0
- package/dist/orm-node/src/db-conn-factory.d.ts.map +1 -0
- package/dist/orm-node/src/index.d.ts +9 -0
- package/dist/orm-node/src/index.d.ts.map +1 -0
- package/dist/orm-node/src/node-db-context-executor.d.ts +77 -0
- package/dist/orm-node/src/node-db-context-executor.d.ts.map +1 -0
- package/dist/orm-node/src/pooled-db-conn.d.ts +79 -0
- package/dist/orm-node/src/pooled-db-conn.d.ts.map +1 -0
- package/dist/orm-node/src/sd-orm.d.ts +78 -0
- package/dist/orm-node/src/sd-orm.d.ts.map +1 -0
- package/dist/orm-node/src/types/db-conn.d.ts +159 -0
- package/dist/orm-node/src/types/db-conn.d.ts.map +1 -0
- package/dist/pooled-db-conn.js +134 -0
- package/dist/pooled-db-conn.js.map +7 -0
- package/dist/sd-orm.js +44 -0
- package/dist/sd-orm.js.map +7 -0
- package/dist/types/db-conn.js +17 -0
- package/dist/types/db-conn.js.map +7 -0
- package/package.json +50 -0
- package/src/connections/mssql-db-conn.ts +483 -0
- package/src/connections/mysql-db-conn.ts +299 -0
- package/src/connections/postgresql-db-conn.ts +254 -0
- package/src/db-conn-factory.ts +114 -0
- package/src/index.ts +13 -0
- package/src/node-db-context-executor.ts +162 -0
- package/src/pooled-db-conn.ts +175 -0
- package/src/sd-orm.ts +102 -0
- package/src/types/db-conn.ts +196 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { SdError } from "@simplysm/core-common";
|
|
2
|
+
import type {
|
|
3
|
+
DbContextExecutor,
|
|
4
|
+
IsolationLevel,
|
|
5
|
+
QueryDef,
|
|
6
|
+
ResultMeta,
|
|
7
|
+
Dialect,
|
|
8
|
+
ColumnMeta,
|
|
9
|
+
DataRecord,
|
|
10
|
+
} from "@simplysm/orm-common";
|
|
11
|
+
import { createQueryBuilder, parseQueryResult } from "@simplysm/orm-common";
|
|
12
|
+
import type { DbConn, DbConnConfig } from "./types/db-conn";
|
|
13
|
+
import { DB_CONN_ERRORS, getDialectFromConfig } from "./types/db-conn";
|
|
14
|
+
import { DbConnFactory } from "./db-conn-factory";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Node.js 환경용 DbContextExecutor
|
|
18
|
+
*
|
|
19
|
+
* DbContext에서 사용하는 실행기로, 실제 DB 연결을 담당한다.
|
|
20
|
+
*/
|
|
21
|
+
export class NodeDbContextExecutor implements DbContextExecutor {
|
|
22
|
+
private _conn?: DbConn;
|
|
23
|
+
private readonly _dialect: Dialect;
|
|
24
|
+
|
|
25
|
+
constructor(private readonly _config: DbConnConfig) {
|
|
26
|
+
this._dialect = getDialectFromConfig(_config);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* DB 연결 수립
|
|
31
|
+
*
|
|
32
|
+
* 커넥션 풀에서 연결을 획득하고 연결 상태를 활성화한다.
|
|
33
|
+
*/
|
|
34
|
+
async connect(): Promise<void> {
|
|
35
|
+
this._conn = await DbConnFactory.create(this._config);
|
|
36
|
+
await this._conn.connect();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* DB 연결 종료
|
|
41
|
+
*
|
|
42
|
+
* 커넥션 풀에 연결을 반환한다.
|
|
43
|
+
*
|
|
44
|
+
* @throws {Error} 연결되지 않은 상태일 때
|
|
45
|
+
*/
|
|
46
|
+
async close(): Promise<void> {
|
|
47
|
+
const conn = this._requireConn();
|
|
48
|
+
await conn.close();
|
|
49
|
+
this._conn = undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 트랜잭션 시작
|
|
54
|
+
*
|
|
55
|
+
* @param isolationLevel - 트랜잭션 격리 수준
|
|
56
|
+
* @throws {Error} 연결되지 않은 상태일 때
|
|
57
|
+
*/
|
|
58
|
+
async beginTransaction(isolationLevel?: IsolationLevel): Promise<void> {
|
|
59
|
+
const conn = this._requireConn();
|
|
60
|
+
await conn.beginTransaction(isolationLevel);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 트랜잭션 커밋
|
|
65
|
+
*
|
|
66
|
+
* @throws {Error} 연결되지 않은 상태일 때
|
|
67
|
+
*/
|
|
68
|
+
async commitTransaction(): Promise<void> {
|
|
69
|
+
const conn = this._requireConn();
|
|
70
|
+
await conn.commitTransaction();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 트랜잭션 롤백
|
|
75
|
+
*
|
|
76
|
+
* @throws {Error} 연결되지 않은 상태일 때
|
|
77
|
+
*/
|
|
78
|
+
async rollbackTransaction(): Promise<void> {
|
|
79
|
+
const conn = this._requireConn();
|
|
80
|
+
await conn.rollbackTransaction();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 파라미터화된 쿼리 실행
|
|
85
|
+
*
|
|
86
|
+
* @param query - SQL 쿼리 문자열
|
|
87
|
+
* @param params - 쿼리 파라미터 배열
|
|
88
|
+
* @returns 쿼리 결과 배열
|
|
89
|
+
* @throws {Error} 연결되지 않은 상태일 때
|
|
90
|
+
*/
|
|
91
|
+
async executeParametrized(query: string, params?: unknown[]): Promise<unknown[][]> {
|
|
92
|
+
const conn = this._requireConn();
|
|
93
|
+
return conn.executeParametrized(query, params);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 대량 데이터 삽입 (네이티브 벌크 API 사용)
|
|
98
|
+
*
|
|
99
|
+
* @param tableName - 대상 테이블명
|
|
100
|
+
* @param columnMetas - 컬럼 메타데이터
|
|
101
|
+
* @param records - 삽입할 레코드 배열
|
|
102
|
+
* @throws {Error} 연결되지 않은 상태일 때
|
|
103
|
+
*/
|
|
104
|
+
async bulkInsert(tableName: string, columnMetas: Record<string, ColumnMeta>, records: DataRecord[]): Promise<void> {
|
|
105
|
+
const conn = this._requireConn();
|
|
106
|
+
await conn.bulkInsert(tableName, columnMetas, records);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* QueryDef 배열 실행
|
|
111
|
+
*
|
|
112
|
+
* QueryDef를 SQL로 변환하여 실행하고, ResultMeta를 사용하여 결과를 파싱한다.
|
|
113
|
+
*
|
|
114
|
+
* @param defs - 실행할 QueryDef 배열
|
|
115
|
+
* @param resultMetas - 결과 파싱용 메타데이터 배열 (타입 변환에 사용)
|
|
116
|
+
* @returns 각 QueryDef의 실행 결과 배열
|
|
117
|
+
* @throws {Error} 연결되지 않은 상태일 때
|
|
118
|
+
*/
|
|
119
|
+
async executeDefs<T = DataRecord>(defs: QueryDef[], resultMetas?: (ResultMeta | undefined)[]): Promise<T[][]> {
|
|
120
|
+
const conn = this._requireConn();
|
|
121
|
+
|
|
122
|
+
const builder = createQueryBuilder(this._dialect);
|
|
123
|
+
|
|
124
|
+
// 가져올 데이터가 없는 것으로 옵션 설정을 했을 때, 하나의 쿼리로 한번의 요청 보냄
|
|
125
|
+
// 결과가 필요 없으므로 defs.length개의 빈 배열을 반환하여 인터페이스 계약 유지
|
|
126
|
+
if (resultMetas != null && resultMetas.every((item) => item == null)) {
|
|
127
|
+
const combinedSql = defs.map((def) => builder.build(def).sql).join("\n");
|
|
128
|
+
await conn.execute([combinedSql]);
|
|
129
|
+
return defs.map(() => []) as T[][];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 각 def를 개별 실행
|
|
133
|
+
const results: T[][] = [];
|
|
134
|
+
for (let i = 0; i < defs.length; i++) {
|
|
135
|
+
const def = defs[i];
|
|
136
|
+
const meta = resultMetas?.[i];
|
|
137
|
+
const buildResult = builder.build(def);
|
|
138
|
+
|
|
139
|
+
const rawResults = await conn.execute([buildResult.sql]);
|
|
140
|
+
|
|
141
|
+
// resultSetIndex가 지정된 경우 해당 인덱스의 결과셋 사용
|
|
142
|
+
const targetResultSet =
|
|
143
|
+
buildResult.resultSetIndex != null ? rawResults[buildResult.resultSetIndex] : rawResults[0];
|
|
144
|
+
|
|
145
|
+
if (meta != null) {
|
|
146
|
+
const parsed = await parseQueryResult<T>(targetResultSet as Record<string, unknown>[], meta);
|
|
147
|
+
results.push(parsed ?? []);
|
|
148
|
+
} else {
|
|
149
|
+
results.push(targetResultSet as T[]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return results;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private _requireConn(): DbConn {
|
|
157
|
+
if (this._conn == null) {
|
|
158
|
+
throw new SdError(DB_CONN_ERRORS.NOT_CONNECTED);
|
|
159
|
+
}
|
|
160
|
+
return this._conn;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { createConsola } from "consola";
|
|
2
|
+
import { SdError, EventEmitter } from "@simplysm/core-common";
|
|
3
|
+
import type { Pool } from "generic-pool";
|
|
4
|
+
import type { ColumnMeta, IsolationLevel } from "@simplysm/orm-common";
|
|
5
|
+
import { DB_CONN_ERRORS, type DbConn, type DbConnConfig } from "./types/db-conn";
|
|
6
|
+
|
|
7
|
+
const logger = createConsola().withTag("pooled-db-conn");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 커넥션 풀에서 관리되는 DB 연결 래퍼
|
|
11
|
+
*
|
|
12
|
+
* generic-pool 라이브러리를 사용하여 커넥션 풀링을 지원한다.
|
|
13
|
+
* 실제 물리 연결은 풀에서 획득하고 반환한다.
|
|
14
|
+
*/
|
|
15
|
+
export class PooledDbConn extends EventEmitter<{ close: void }> implements DbConn {
|
|
16
|
+
// 풀에서 빌려온 실제 물리 커넥션
|
|
17
|
+
private _rawConn?: DbConn;
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
private readonly _pool: Pool<DbConn>,
|
|
21
|
+
private readonly _initialConfig: DbConnConfig,
|
|
22
|
+
) {
|
|
23
|
+
super();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// [Property] config
|
|
27
|
+
get config(): DbConnConfig {
|
|
28
|
+
return this._rawConn?.config ?? this._initialConfig;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// [Property] isConnected
|
|
32
|
+
get isConnected(): boolean {
|
|
33
|
+
return this._rawConn?.isConnected ?? false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// [Property] isOnTransaction
|
|
37
|
+
get isOnTransaction(): boolean {
|
|
38
|
+
return this._rawConn?.isOnTransaction ?? false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 풀에서 DB 연결을 획득한다.
|
|
43
|
+
*
|
|
44
|
+
* @throws {SdError} 이미 연결된 상태일 때
|
|
45
|
+
*/
|
|
46
|
+
async connect(): Promise<void> {
|
|
47
|
+
if (this._rawConn != null) {
|
|
48
|
+
throw new SdError(DB_CONN_ERRORS.ALREADY_CONNECTED);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 1. 풀에서 커넥션 획득
|
|
52
|
+
this._rawConn = await this._pool.acquire();
|
|
53
|
+
|
|
54
|
+
// 2. 물리 연결이 (타임아웃 등으로) 끊어질 경우를 대비해 리스너 등록
|
|
55
|
+
// 만약 사용 중에 끊기면 PooledDbConn도 close 이벤트를 발생시켜야 함
|
|
56
|
+
this._rawConn.on("close", this._onRawConnClose);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 풀에 DB 연결을 반환한다. (실제 연결을 종료하지 않음)
|
|
61
|
+
*/
|
|
62
|
+
async close(): Promise<void> {
|
|
63
|
+
if (this._rawConn != null) {
|
|
64
|
+
// 1. 트랜잭션 진행 중이면 롤백하여 깨끗한 상태로 풀에 반환
|
|
65
|
+
if (this._rawConn.isOnTransaction) {
|
|
66
|
+
try {
|
|
67
|
+
await this._rawConn.rollbackTransaction();
|
|
68
|
+
} catch (err) {
|
|
69
|
+
// 롤백 실패 시 로그만 남기고 계속 진행 (연결이 이미 끊긴 경우 등)
|
|
70
|
+
logger.warn("풀 반환 시 롤백 실패", err instanceof Error ? err.message : String(err));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. 리스너 해제 (Pool에 돌아가서 다른 래퍼에 의해 재사용될 때 영향 주지 않도록)
|
|
75
|
+
this._rawConn.off("close", this._onRawConnClose);
|
|
76
|
+
|
|
77
|
+
// 3. 풀에 커넥션 반환 (실제로 끊지 않음)
|
|
78
|
+
await this._pool.release(this._rawConn);
|
|
79
|
+
this._rawConn = undefined;
|
|
80
|
+
|
|
81
|
+
// 4. 소비자에게 논리적으로 연결이 닫혔음을 알림
|
|
82
|
+
this.emit("close");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 물리 연결이 끊겼을 때 처리 핸들러
|
|
87
|
+
private readonly _onRawConnClose = () => {
|
|
88
|
+
// 물리 연결이 끊겼으므로 참조 제거 (Pool에서는 validate 시점에 걸러낼 것임)
|
|
89
|
+
this._rawConn = undefined;
|
|
90
|
+
// 소비자에게 알림
|
|
91
|
+
this.emit("close");
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// --- 아래는 위임(Delegation) 메소드 ---
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 트랜잭션 시작
|
|
98
|
+
*
|
|
99
|
+
* @param isolationLevel - 트랜잭션 격리 수준
|
|
100
|
+
* @throws {SdError} 연결이 획득되지 않은 상태일 때
|
|
101
|
+
*/
|
|
102
|
+
async beginTransaction(isolationLevel?: IsolationLevel): Promise<void> {
|
|
103
|
+
const conn = this._requireRawConn();
|
|
104
|
+
await conn.beginTransaction(isolationLevel);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 트랜잭션 커밋
|
|
109
|
+
*
|
|
110
|
+
* @throws {SdError} 연결이 획득되지 않은 상태일 때
|
|
111
|
+
*/
|
|
112
|
+
async commitTransaction(): Promise<void> {
|
|
113
|
+
const conn = this._requireRawConn();
|
|
114
|
+
await conn.commitTransaction();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 트랜잭션 롤백
|
|
119
|
+
*
|
|
120
|
+
* @throws {SdError} 연결이 획득되지 않은 상태일 때
|
|
121
|
+
*/
|
|
122
|
+
async rollbackTransaction(): Promise<void> {
|
|
123
|
+
const conn = this._requireRawConn();
|
|
124
|
+
await conn.rollbackTransaction();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* SQL 쿼리 실행
|
|
129
|
+
*
|
|
130
|
+
* @param queries - 실행할 SQL 쿼리 배열
|
|
131
|
+
* @returns 각 쿼리의 결과 배열
|
|
132
|
+
* @throws {SdError} 연결이 획득되지 않은 상태일 때
|
|
133
|
+
*/
|
|
134
|
+
async execute(queries: string[]): Promise<unknown[][]> {
|
|
135
|
+
const conn = this._requireRawConn();
|
|
136
|
+
return conn.execute(queries);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 파라미터화된 SQL 쿼리 실행
|
|
141
|
+
*
|
|
142
|
+
* @param query - SQL 쿼리 문자열
|
|
143
|
+
* @param params - 쿼리 파라미터 배열
|
|
144
|
+
* @returns 쿼리 결과 배열
|
|
145
|
+
* @throws {SdError} 연결이 획득되지 않은 상태일 때
|
|
146
|
+
*/
|
|
147
|
+
async executeParametrized(query: string, params?: unknown[]): Promise<unknown[][]> {
|
|
148
|
+
const conn = this._requireRawConn();
|
|
149
|
+
return conn.executeParametrized(query, params);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 대량 데이터 삽입 (네이티브 벌크 API 사용)
|
|
154
|
+
*
|
|
155
|
+
* @param tableName - 대상 테이블명
|
|
156
|
+
* @param columnMetas - 컬럼 메타데이터
|
|
157
|
+
* @param records - 삽입할 레코드 배열
|
|
158
|
+
* @throws {SdError} 연결이 획득되지 않은 상태일 때
|
|
159
|
+
*/
|
|
160
|
+
async bulkInsert(
|
|
161
|
+
tableName: string,
|
|
162
|
+
columnMetas: Record<string, ColumnMeta>,
|
|
163
|
+
records: Record<string, unknown>[],
|
|
164
|
+
): Promise<void> {
|
|
165
|
+
const conn = this._requireRawConn();
|
|
166
|
+
await conn.bulkInsert(tableName, columnMetas, records);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private _requireRawConn(): DbConn {
|
|
170
|
+
if (this._rawConn == null) {
|
|
171
|
+
throw new SdError(`${DB_CONN_ERRORS.NOT_CONNECTED} (Pool Connection is not acquired)`);
|
|
172
|
+
}
|
|
173
|
+
return this._rawConn;
|
|
174
|
+
}
|
|
175
|
+
}
|
package/src/sd-orm.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { DbContext, IsolationLevel } from "@simplysm/orm-common";
|
|
2
|
+
import type { Type } from "@simplysm/core-common";
|
|
3
|
+
import type { DbConnConfig } from "./types/db-conn";
|
|
4
|
+
import { NodeDbContextExecutor } from "./node-db-context-executor";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* SdOrm 옵션
|
|
8
|
+
*
|
|
9
|
+
* DbConnConfig보다 우선 적용되는 DbContext 옵션
|
|
10
|
+
*/
|
|
11
|
+
export interface SdOrmOptions {
|
|
12
|
+
/**
|
|
13
|
+
* 데이터베이스 이름 (DbConnConfig의 database 대신 사용)
|
|
14
|
+
*/
|
|
15
|
+
database?: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 스키마 이름 (MSSQL: dbo, PostgreSQL: public)
|
|
19
|
+
*/
|
|
20
|
+
schema?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Node.js ORM 클래스
|
|
25
|
+
*
|
|
26
|
+
* DbContext와 DB 연결을 관리하는 최상위 클래스입니다.
|
|
27
|
+
* DbContext 타입과 연결 설정을 받아 트랜잭션을 관리합니다.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* class MyDb extends DbContext {
|
|
32
|
+
* readonly user = queryable(this, User);
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* const orm = new SdOrm(MyDb, {
|
|
36
|
+
* dialect: "mysql",
|
|
37
|
+
* host: "localhost",
|
|
38
|
+
* port: 3306,
|
|
39
|
+
* username: "root",
|
|
40
|
+
* password: "password",
|
|
41
|
+
* database: "mydb",
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* // 트랜잭션 내에서 실행
|
|
45
|
+
* await orm.connect(async (db) => {
|
|
46
|
+
* const users = await db.user().result();
|
|
47
|
+
* return users;
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* // 트랜잭션 없이 실행
|
|
51
|
+
* await orm.connectWithoutTransaction(async (db) => {
|
|
52
|
+
* const users = await db.user().result();
|
|
53
|
+
* return users;
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export class SdOrm<T extends DbContext> {
|
|
58
|
+
constructor(
|
|
59
|
+
readonly dbContextType: Type<T>,
|
|
60
|
+
readonly config: DbConnConfig,
|
|
61
|
+
readonly options?: SdOrmOptions,
|
|
62
|
+
) {}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 트랜잭션 내에서 콜백 실행
|
|
66
|
+
*
|
|
67
|
+
* @param callback - DB 연결 후 실행할 콜백
|
|
68
|
+
* @param isolationLevel - 트랜잭션 격리 수준
|
|
69
|
+
* @returns 콜백 결과
|
|
70
|
+
*/
|
|
71
|
+
async connect<R>(callback: (conn: T) => Promise<R>, isolationLevel?: IsolationLevel): Promise<R> {
|
|
72
|
+
const db = this._createDbContext();
|
|
73
|
+
return db.connect(async () => callback(db), isolationLevel);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 트랜잭션 없이 콜백 실행
|
|
78
|
+
*
|
|
79
|
+
* @param callback - DB 연결 후 실행할 콜백
|
|
80
|
+
* @returns 콜백 결과
|
|
81
|
+
*/
|
|
82
|
+
async connectWithoutTransaction<R>(callback: (conn: T) => Promise<R>): Promise<R> {
|
|
83
|
+
const db = this._createDbContext();
|
|
84
|
+
return db.connectWithoutTransaction(async () => callback(db));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* DbContext 인스턴스 생성
|
|
89
|
+
*/
|
|
90
|
+
private _createDbContext(): T {
|
|
91
|
+
// database는 options에서 우선, 없으면 config에서
|
|
92
|
+
const database = this.options?.database ?? ("database" in this.config ? this.config.database : undefined);
|
|
93
|
+
|
|
94
|
+
// schema는 options에서 우선, 없으면 config에서
|
|
95
|
+
const schema = this.options?.schema ?? ("schema" in this.config ? this.config.schema : undefined);
|
|
96
|
+
|
|
97
|
+
return new this.dbContextType(new NodeDbContextExecutor(this.config), {
|
|
98
|
+
database,
|
|
99
|
+
schema,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { EventEmitter } from "@simplysm/core-common";
|
|
2
|
+
import type { ColumnMeta, Dialect, IsolationLevel } from "@simplysm/orm-common";
|
|
3
|
+
|
|
4
|
+
// ============================================
|
|
5
|
+
// 공통 상수
|
|
6
|
+
// ============================================
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* DB 연결 기본 타임아웃 (10분)
|
|
10
|
+
*/
|
|
11
|
+
export const DB_CONN_DEFAULT_TIMEOUT = 10 * 60 * 1000;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* DB 연결 에러 메시지
|
|
15
|
+
*/
|
|
16
|
+
export const DB_CONN_ERRORS = {
|
|
17
|
+
NOT_CONNECTED: "'Connection'이 연결되어있지 않습니다.",
|
|
18
|
+
ALREADY_CONNECTED: "이미 'Connection'이 연결되어있습니다.",
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
// ============================================
|
|
22
|
+
// IDbConn Interface
|
|
23
|
+
// ============================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 저수준 DB 연결 인터페이스
|
|
27
|
+
*
|
|
28
|
+
* 각 DBMS별 구현체가 이 인터페이스를 구현합니다.
|
|
29
|
+
* - {@link MysqlDbConn} - MySQL 연결
|
|
30
|
+
* - {@link MssqlDbConn} - MSSQL 연결
|
|
31
|
+
* - {@link PostgresqlDbConn} - PostgreSQL 연결
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* SdEventEmitter를 상속하여 'close' 이벤트를 발생시킵니다.
|
|
35
|
+
*/
|
|
36
|
+
export interface DbConn extends EventEmitter<{ close: void }> {
|
|
37
|
+
/**
|
|
38
|
+
* 연결 설정
|
|
39
|
+
*/
|
|
40
|
+
config: DbConnConfig;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 연결 여부
|
|
44
|
+
*/
|
|
45
|
+
isConnected: boolean;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 트랜잭션 진행 여부
|
|
49
|
+
*/
|
|
50
|
+
isOnTransaction: boolean;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* DB 연결 수립
|
|
54
|
+
*/
|
|
55
|
+
connect(): Promise<void>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* DB 연결 종료
|
|
59
|
+
*/
|
|
60
|
+
close(): Promise<void>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 트랜잭션 시작
|
|
64
|
+
*
|
|
65
|
+
* @param isolationLevel - 격리 수준 (선택)
|
|
66
|
+
*/
|
|
67
|
+
beginTransaction(isolationLevel?: IsolationLevel): Promise<void>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 트랜잭션 커밋
|
|
71
|
+
*/
|
|
72
|
+
commitTransaction(): Promise<void>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 트랜잭션 롤백
|
|
76
|
+
*/
|
|
77
|
+
rollbackTransaction(): Promise<void>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* SQL 쿼리 배열 실행
|
|
81
|
+
*
|
|
82
|
+
* @param queries - 실행할 SQL 문자열 배열
|
|
83
|
+
* @returns 각 쿼리별 결과 배열의 배열
|
|
84
|
+
*/
|
|
85
|
+
execute(queries: string[]): Promise<unknown[][]>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 파라미터화된 쿼리 실행
|
|
89
|
+
*
|
|
90
|
+
* @param query - SQL 쿼리 문자열
|
|
91
|
+
* @param params - 바인딩 파라미터 (선택)
|
|
92
|
+
* @returns 결과 배열의 배열
|
|
93
|
+
*/
|
|
94
|
+
executeParametrized(query: string, params?: unknown[]): Promise<unknown[][]>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 대량 INSERT (네이티브 벌크 API 사용)
|
|
98
|
+
*
|
|
99
|
+
* - MSSQL: tedious BulkLoad
|
|
100
|
+
* - MySQL: LOAD DATA LOCAL INFILE (임시 파일)
|
|
101
|
+
* - PostgreSQL: COPY FROM STDIN
|
|
102
|
+
*
|
|
103
|
+
* @param tableName - 테이블명 (database.table 또는 database.schema.table)
|
|
104
|
+
* @param columnMetas - 컬럼명 → ColumnMeta 매핑
|
|
105
|
+
* @param records - 삽입할 레코드 배열
|
|
106
|
+
*/
|
|
107
|
+
bulkInsert(
|
|
108
|
+
tableName: string,
|
|
109
|
+
columnMetas: Record<string, ColumnMeta>,
|
|
110
|
+
records: Record<string, unknown>[],
|
|
111
|
+
): Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================
|
|
115
|
+
// DbConnConfig Types
|
|
116
|
+
// ============================================
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 커넥션 풀 설정
|
|
120
|
+
*
|
|
121
|
+
* @remarks
|
|
122
|
+
* 각 값의 기본값:
|
|
123
|
+
* - min: 1 (최소 연결 수)
|
|
124
|
+
* - max: 10 (최대 연결 수)
|
|
125
|
+
* - acquireTimeoutMillis: 30000 (연결 획득 타임아웃)
|
|
126
|
+
* - idleTimeoutMillis: 30000 (유휴 연결 타임아웃)
|
|
127
|
+
*/
|
|
128
|
+
export interface DbPoolConfig {
|
|
129
|
+
/** 최소 연결 수 (기본: 1) */
|
|
130
|
+
min?: number;
|
|
131
|
+
/** 최대 연결 수 (기본: 10) */
|
|
132
|
+
max?: number;
|
|
133
|
+
/** 연결 획득 타임아웃 (밀리초, 기본: 30000) */
|
|
134
|
+
acquireTimeoutMillis?: number;
|
|
135
|
+
/** 유휴 연결 타임아웃 (밀리초, 기본: 30000) */
|
|
136
|
+
idleTimeoutMillis?: number;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* DB 연결 설정 타입 (dialect별 분기)
|
|
141
|
+
*/
|
|
142
|
+
export type DbConnConfig = MysqlDbConnConfig | MssqlDbConnConfig | PostgresqlDbConnConfig;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* MySQL 연결 설정
|
|
146
|
+
*/
|
|
147
|
+
export interface MysqlDbConnConfig {
|
|
148
|
+
dialect: "mysql";
|
|
149
|
+
host: string;
|
|
150
|
+
port?: number;
|
|
151
|
+
username: string;
|
|
152
|
+
password: string;
|
|
153
|
+
database?: string;
|
|
154
|
+
defaultIsolationLevel?: IsolationLevel;
|
|
155
|
+
pool?: DbPoolConfig;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* MSSQL 연결 설정
|
|
160
|
+
*/
|
|
161
|
+
export interface MssqlDbConnConfig {
|
|
162
|
+
dialect: "mssql" | "mssql-azure";
|
|
163
|
+
host: string;
|
|
164
|
+
port?: number;
|
|
165
|
+
username: string;
|
|
166
|
+
password: string;
|
|
167
|
+
database?: string;
|
|
168
|
+
schema?: string;
|
|
169
|
+
defaultIsolationLevel?: IsolationLevel;
|
|
170
|
+
pool?: DbPoolConfig;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* PostgreSQL 연결 설정
|
|
175
|
+
*/
|
|
176
|
+
export interface PostgresqlDbConnConfig {
|
|
177
|
+
dialect: "postgresql";
|
|
178
|
+
host: string;
|
|
179
|
+
port?: number;
|
|
180
|
+
username: string;
|
|
181
|
+
password: string;
|
|
182
|
+
database?: string;
|
|
183
|
+
schema?: string;
|
|
184
|
+
defaultIsolationLevel?: IsolationLevel;
|
|
185
|
+
pool?: DbPoolConfig;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* DbConnConfig에서 Dialect 추출
|
|
190
|
+
*/
|
|
191
|
+
export function getDialectFromConfig(config: DbConnConfig): Dialect {
|
|
192
|
+
if (config.dialect === "mssql-azure") {
|
|
193
|
+
return "mssql";
|
|
194
|
+
}
|
|
195
|
+
return config.dialect;
|
|
196
|
+
}
|