metal-orm 1.0.13 → 1.0.15
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/README.md +75 -82
- package/dist/decorators/index.cjs +1600 -27
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +6 -2
- package/dist/decorators/index.d.ts +6 -2
- package/dist/decorators/index.js +1599 -27
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +4608 -3429
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +511 -159
- package/dist/index.d.ts +511 -159
- package/dist/index.js +4526 -3415
- package/dist/index.js.map +1 -1
- package/dist/{select-CCp1oz9p.d.cts → select-Bkv8g8u_.d.cts} +193 -67
- package/dist/{select-CCp1oz9p.d.ts → select-Bkv8g8u_.d.ts} +193 -67
- package/package.json +1 -1
- package/src/codegen/typescript.ts +38 -35
- package/src/core/ast/adapters.ts +21 -0
- package/src/core/ast/aggregate-functions.ts +13 -13
- package/src/core/ast/builders.ts +56 -43
- package/src/core/ast/expression-builders.ts +34 -34
- package/src/core/ast/expression-nodes.ts +18 -16
- package/src/core/ast/expression-visitor.ts +122 -69
- package/src/core/ast/expression.ts +6 -4
- package/src/core/ast/join-metadata.ts +15 -0
- package/src/core/ast/join-node.ts +22 -20
- package/src/core/ast/join.ts +5 -5
- package/src/core/ast/query.ts +52 -88
- package/src/core/ast/types.ts +20 -0
- package/src/core/ast/window-functions.ts +55 -55
- package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
- package/src/core/ddl/introspect/context.ts +9 -0
- package/src/core/ddl/introspect/functions/postgres.ts +26 -0
- package/src/core/ddl/introspect/mssql.ts +149 -149
- package/src/core/ddl/introspect/mysql.ts +99 -99
- package/src/core/ddl/introspect/postgres.ts +245 -154
- package/src/core/ddl/introspect/registry.ts +26 -0
- package/src/core/ddl/introspect/run-select.ts +25 -0
- package/src/core/ddl/introspect/sqlite.ts +7 -7
- package/src/core/ddl/introspect/types.ts +23 -19
- package/src/core/ddl/introspect/utils.ts +1 -1
- package/src/core/ddl/naming-strategy.ts +10 -0
- package/src/core/ddl/schema-dialect.ts +41 -0
- package/src/core/ddl/schema-diff.ts +211 -179
- package/src/core/ddl/schema-generator.ts +16 -90
- package/src/core/ddl/schema-introspect.ts +25 -32
- package/src/core/ddl/schema-plan-executor.ts +17 -0
- package/src/core/ddl/schema-types.ts +46 -39
- package/src/core/ddl/sql-writing.ts +170 -0
- package/src/core/dialect/abstract.ts +144 -126
- package/src/core/dialect/base/cte-compiler.ts +33 -0
- package/src/core/dialect/base/function-table-formatter.ts +132 -0
- package/src/core/dialect/base/groupby-compiler.ts +21 -0
- package/src/core/dialect/base/join-compiler.ts +26 -0
- package/src/core/dialect/base/orderby-compiler.ts +21 -0
- package/src/core/dialect/base/pagination-strategy.ts +32 -0
- package/src/core/dialect/base/returning-strategy.ts +56 -0
- package/src/core/dialect/base/sql-dialect.ts +181 -204
- package/src/core/dialect/dialect-factory.ts +91 -0
- package/src/core/dialect/mssql/functions.ts +101 -0
- package/src/core/dialect/mssql/index.ts +128 -126
- package/src/core/dialect/mysql/functions.ts +101 -0
- package/src/core/dialect/mysql/index.ts +20 -18
- package/src/core/dialect/postgres/functions.ts +95 -0
- package/src/core/dialect/postgres/index.ts +30 -28
- package/src/core/dialect/sqlite/functions.ts +115 -0
- package/src/core/dialect/sqlite/index.ts +30 -28
- package/src/core/driver/database-driver.ts +11 -0
- package/src/core/driver/mssql-driver.ts +20 -0
- package/src/core/driver/mysql-driver.ts +20 -0
- package/src/core/driver/postgres-driver.ts +20 -0
- package/src/core/driver/sqlite-driver.ts +20 -0
- package/src/core/execution/db-executor.ts +63 -0
- package/src/core/execution/executors/mssql-executor.ts +39 -0
- package/src/core/execution/executors/mysql-executor.ts +47 -0
- package/src/core/execution/executors/postgres-executor.ts +32 -0
- package/src/core/execution/executors/sqlite-executor.ts +31 -0
- package/src/core/functions/datetime.ts +132 -0
- package/src/core/functions/numeric.ts +179 -0
- package/src/core/functions/standard-strategy.ts +47 -0
- package/src/core/functions/text.ts +147 -0
- package/src/core/functions/types.ts +18 -0
- package/src/core/hydration/types.ts +57 -0
- package/src/decorators/bootstrap.ts +10 -0
- package/src/decorators/relations.ts +15 -0
- package/src/index.ts +30 -19
- package/src/orm/entity-metadata.ts +7 -0
- package/src/orm/entity.ts +58 -27
- package/src/orm/hydration.ts +25 -17
- package/src/orm/lazy-batch.ts +46 -2
- package/src/orm/orm-context.ts +60 -60
- package/src/orm/query-logger.ts +1 -1
- package/src/orm/relation-change-processor.ts +43 -2
- package/src/orm/relations/has-one.ts +139 -0
- package/src/orm/transaction-runner.ts +1 -1
- package/src/orm/unit-of-work.ts +60 -60
- package/src/query-builder/delete.ts +22 -5
- package/src/query-builder/hydration-manager.ts +2 -1
- package/src/query-builder/hydration-planner.ts +8 -7
- package/src/query-builder/insert.ts +22 -5
- package/src/query-builder/relation-conditions.ts +9 -8
- package/src/query-builder/relation-service.ts +3 -2
- package/src/query-builder/select.ts +66 -61
- package/src/query-builder/update.ts +22 -5
- package/src/schema/column.ts +246 -246
- package/src/schema/relation.ts +35 -1
- package/src/schema/table.ts +28 -28
- package/src/schema/types.ts +41 -31
- package/src/orm/db-executor.ts +0 -11
package/README.md
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
# MetalORM ⚙️
|
|
1
|
+
# MetalORM ⚙️ - Type-safe SQL, layered ORM, decorator-based entities – all on the same core.
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/metal-orm)
|
|
4
4
|
[](https://github.com/celsowm/metal-orm/blob/main/LICENSE)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
|
|
7
|
-
**Type-safe SQL, layered ORM, decorator-based entities – all on the same core.**
|
|
8
|
-
|
|
9
7
|
MetalORM is a TypeScript-first, AST-driven SQL toolkit you can dial up or down depending on how “ORM-y” you want to be:
|
|
10
8
|
|
|
11
9
|
- **Level 1 – Query builder & hydration 🧩**
|
|
@@ -22,10 +20,10 @@ Use only the layer you need in each part of your codebase.
|
|
|
22
20
|
<a id="table-of-contents"></a>
|
|
23
21
|
## Table of Contents 🧭
|
|
24
22
|
|
|
25
|
-
- [Documentation](#documentation)
|
|
26
|
-
- [Features](#features)
|
|
27
|
-
- [Installation](#installation)
|
|
28
|
-
- [Quick start - three levels](#quick-start)
|
|
23
|
+
- [Documentation](#documentation)
|
|
24
|
+
- [Features](#features)
|
|
25
|
+
- [Installation](#installation)
|
|
26
|
+
- [Quick start - three levels](#quick-start)
|
|
29
27
|
- [Level 1 – Query builder & hydration](#level-1)
|
|
30
28
|
- [Level 2 – Entities + Unit of Work](#level-2)
|
|
31
29
|
- [Level 3 – Decorator entities](#level-3)
|
|
@@ -41,18 +39,18 @@ Use only the layer you need in each part of your codebase.
|
|
|
41
39
|
|
|
42
40
|
Full docs live in the `docs/` folder:
|
|
43
41
|
|
|
44
|
-
- [Introduction](https://github.com/celsowm/metal-orm/blob/main/docs/index.md)
|
|
45
|
-
- [Getting Started](https://github.com/celsowm/metal-orm/blob/main/docs/getting-started.md)
|
|
46
|
-
- [Level 3 Backend Tutorial](https://github.com/celsowm/metal-orm/blob/main/docs/level-3-backend-tutorial.md)
|
|
47
|
-
- [Schema Definition](https://github.com/celsowm/metal-orm/blob/main/docs/schema-definition.md)
|
|
48
|
-
- [Query Builder](https://github.com/celsowm/metal-orm/blob/main/docs/query-builder.md)
|
|
49
|
-
- [DML Operations](https://github.com/celsowm/metal-orm/blob/main/docs/dml-operations.md)
|
|
50
|
-
- [Hydration & Entities](https://github.com/celsowm/metal-orm/blob/main/docs/hydration.md)
|
|
51
|
-
- [Runtime & Unit of Work](https://github.com/celsowm/metal-orm/blob/main/docs/runtime.md)
|
|
52
|
-
- [Advanced Features](https://github.com/celsowm/metal-orm/blob/main/docs/advanced-features.md)
|
|
53
|
-
- [Multi-Dialect Support](https://github.com/celsowm/metal-orm/blob/main/docs/multi-dialect-support.md)
|
|
54
|
-
- [Schema Generation (DDL)](https://github.com/celsowm/metal-orm/blob/main/docs/schema-generation.md)
|
|
55
|
-
- [API Reference](https://github.com/celsowm/metal-orm/blob/main/docs/api-reference.md)
|
|
42
|
+
- [Introduction](https://github.com/celsowm/metal-orm/blob/main/docs/index.md)
|
|
43
|
+
- [Getting Started](https://github.com/celsowm/metal-orm/blob/main/docs/getting-started.md)
|
|
44
|
+
- [Level 3 Backend Tutorial](https://github.com/celsowm/metal-orm/blob/main/docs/level-3-backend-tutorial.md)
|
|
45
|
+
- [Schema Definition](https://github.com/celsowm/metal-orm/blob/main/docs/schema-definition.md)
|
|
46
|
+
- [Query Builder](https://github.com/celsowm/metal-orm/blob/main/docs/query-builder.md)
|
|
47
|
+
- [DML Operations](https://github.com/celsowm/metal-orm/blob/main/docs/dml-operations.md)
|
|
48
|
+
- [Hydration & Entities](https://github.com/celsowm/metal-orm/blob/main/docs/hydration.md)
|
|
49
|
+
- [Runtime & Unit of Work](https://github.com/celsowm/metal-orm/blob/main/docs/runtime.md)
|
|
50
|
+
- [Advanced Features](https://github.com/celsowm/metal-orm/blob/main/docs/advanced-features.md)
|
|
51
|
+
- [Multi-Dialect Support](https://github.com/celsowm/metal-orm/blob/main/docs/multi-dialect-support.md)
|
|
52
|
+
- [Schema Generation (DDL)](https://github.com/celsowm/metal-orm/blob/main/docs/schema-generation.md)
|
|
53
|
+
- [API Reference](https://github.com/celsowm/metal-orm/blob/main/docs/api-reference.md)
|
|
56
54
|
|
|
57
55
|
---
|
|
58
56
|
|
|
@@ -62,16 +60,17 @@ Full docs live in the `docs/` folder:
|
|
|
62
60
|
### Level 1 – Query builder & hydration
|
|
63
61
|
|
|
64
62
|
- **Declarative schema definition** with `defineTable`, `col.*`, and typed relations.
|
|
65
|
-
- **Fluent query builder** over a real SQL AST
|
|
66
|
-
(`SelectQueryBuilder`, `InsertQueryBuilder`, `UpdateQueryBuilder`, `DeleteQueryBuilder`).
|
|
67
|
-
- **Advanced SQL**: CTEs, aggregates, window functions, subqueries, JSON, CASE, EXISTS.
|
|
68
|
-
- **
|
|
69
|
-
- **
|
|
70
|
-
- **
|
|
71
|
-
- **
|
|
72
|
-
- **
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
- **Fluent query builder** over a real SQL AST
|
|
64
|
+
(`SelectQueryBuilder`, `InsertQueryBuilder`, `UpdateQueryBuilder`, `DeleteQueryBuilder`).
|
|
65
|
+
- **Advanced SQL**: CTEs, aggregates, window functions, subqueries, JSON, CASE, EXISTS.
|
|
66
|
+
- **String helpers**: `lower`, `upper`, `trim`, `ltrim/rtrim`, `concat/concatWs`, `substr/left/right`, `position/instr/locate`, `replace`, `repeat`, `lpad/rpad`, `space`, and more with dialect-aware rendering.
|
|
67
|
+
- **Set operations**: `union`, `unionAll`, `intersect`, `except` across all dialects (ORDER/LIMIT apply to the combined result; hydration is disabled for compound queries so rows are returned as-is without collapsing duplicates).
|
|
68
|
+
- **Expression builders**: `eq`, `and`, `or`, `between`, `inList`, `exists`, `jsonPath`, `caseWhen`, window functions like `rowNumber`, `rank`, `lag`, `lead`, etc., all backed by typed AST nodes.
|
|
69
|
+
- **Relation-aware hydration**: turn flat rows into nested objects (`user.posts`, `user.roles`, etc.) using a hydration plan derived from the AST metadata.
|
|
70
|
+
- **Multi-dialect**: compile once, run on MySQL/MariaDB, PostgreSQL, SQLite, or SQL Server via pluggable dialects.
|
|
71
|
+
- **DML**: type-safe INSERT / UPDATE / DELETE with `RETURNING` where supported.
|
|
72
|
+
|
|
73
|
+
Level 1 is ideal when you:
|
|
75
74
|
|
|
76
75
|
- Already have a domain model and just want a serious SQL builder.
|
|
77
76
|
- Want deterministic SQL (no magical query generation).
|
|
@@ -83,13 +82,13 @@ On top of the query builder, MetalORM ships a focused runtime:
|
|
|
83
82
|
|
|
84
83
|
- **Entities inferred from your `TableDef`s** (no separate mapping file).
|
|
85
84
|
- **Lazy, batched relations**: `user.posts.load()`, `user.roles.syncByIds([...])`, etc.
|
|
86
|
-
- **Identity map**: the same row becomes the same entity instance within a context (see the [Identity map pattern](https://en.wikipedia.org/wiki/Identity_map_pattern)).
|
|
87
|
-
- **Unit of Work (`OrmContext`)** tracking New/Dirty/Removed entities and relation changes, inspired by the classic [Unit of Work pattern](https://en.wikipedia.org/wiki/Unit_of_work).
|
|
88
|
-
- **Graph persistence**: mutate a whole object graph and flush once with `ctx.saveChanges()`.
|
|
89
|
-
- **Relation change processor** that knows how to deal with has-many and many-to-many pivot tables.
|
|
90
|
-
- **Interceptors**: `beforeFlush` / `afterFlush` hooks for cross-cutting concerns (auditing, multi-tenant filters, soft delete filters, etc.).
|
|
91
|
-
- **Domain events**: `addDomainEvent` and a DomainEventBus integrated into `saveChanges()`, aligned with domain events from [Domain-driven design](https://en.wikipedia.org/wiki/Domain-driven_design).
|
|
92
|
-
- **JSON-safe entities**: relation wrappers hide internal references and implement `toJSON`, so `JSON.stringify` of hydrated entities works without circular reference errors.
|
|
85
|
+
- **Identity map**: the same row becomes the same entity instance within a context (see the [Identity map pattern](https://en.wikipedia.org/wiki/Identity_map_pattern)).
|
|
86
|
+
- **Unit of Work (`OrmContext`)** tracking New/Dirty/Removed entities and relation changes, inspired by the classic [Unit of Work pattern](https://en.wikipedia.org/wiki/Unit_of_work).
|
|
87
|
+
- **Graph persistence**: mutate a whole object graph and flush once with `ctx.saveChanges()`.
|
|
88
|
+
- **Relation change processor** that knows how to deal with has-many and many-to-many pivot tables.
|
|
89
|
+
- **Interceptors**: `beforeFlush` / `afterFlush` hooks for cross-cutting concerns (auditing, multi-tenant filters, soft delete filters, etc.).
|
|
90
|
+
- **Domain events**: `addDomainEvent` and a DomainEventBus integrated into `saveChanges()`, aligned with domain events from [Domain-driven design](https://en.wikipedia.org/wiki/Domain-driven_design).
|
|
91
|
+
- **JSON-safe entities**: relation wrappers hide internal references and implement `toJSON`, so `JSON.stringify` of hydrated entities works without circular reference errors.
|
|
93
92
|
|
|
94
93
|
Use this layer where:
|
|
95
94
|
|
|
@@ -104,9 +103,10 @@ If you like explicit model classes, you can add a thin decorator layer on top of
|
|
|
104
103
|
- `@Column(...)` and `@PrimaryKey(...)` on properties; decorators collect column metadata and later build `TableDef`s from it.
|
|
105
104
|
- Relation decorators:
|
|
106
105
|
- `@HasMany({ target, foreignKey, ... })`
|
|
106
|
+
- `@HasOne({ target, foreignKey, ... })`
|
|
107
107
|
- `@BelongsTo({ target, foreignKey, ... })`
|
|
108
108
|
- `@BelongsToMany({ target, pivotTable, ... })`
|
|
109
|
-
- `bootstrapEntities()` scans metadata, builds `TableDef`s, wires relations with the same `hasMany` / `belongsTo` / `belongsToMany` helpers you would use manually, and returns the resulting tables.
|
|
109
|
+
- `bootstrapEntities()` scans metadata, builds `TableDef`s, wires relations with the same `hasOne` / `hasMany` / `belongsTo` / `belongsToMany` helpers you would use manually, and returns the resulting tables.
|
|
110
110
|
- `selectFromEntity(MyEntity)` lets you start a `SelectQueryBuilder` directly from the class.
|
|
111
111
|
|
|
112
112
|
You don’t have to use decorators, but when you do, you’re still on the same AST + dialect + runtime foundation.
|
|
@@ -173,10 +173,13 @@ import {
|
|
|
173
173
|
|
|
174
174
|
// 1) A very small table
|
|
175
175
|
const todos = defineTable('todos', {
|
|
176
|
-
id: col.
|
|
177
|
-
title: col.varchar(255)
|
|
178
|
-
done: col.boolean()
|
|
176
|
+
id: col.primaryKey(col.int()),
|
|
177
|
+
title: col.varchar(255),
|
|
178
|
+
done: col.boolean(),
|
|
179
179
|
});
|
|
180
|
+
// Add constraints
|
|
181
|
+
todos.columns.title.notNull = true;
|
|
182
|
+
todos.columns.done.default = false;
|
|
180
183
|
|
|
181
184
|
// 2) Build a simple query
|
|
182
185
|
const listOpenTodos = new SelectQueryBuilder(todos)
|
|
@@ -222,20 +225,29 @@ import {
|
|
|
222
225
|
} from 'metal-orm';
|
|
223
226
|
|
|
224
227
|
const posts = defineTable('posts', {
|
|
225
|
-
id: col.
|
|
226
|
-
title: col.varchar(255)
|
|
227
|
-
userId: col.int()
|
|
228
|
-
createdAt: col.timestamp()
|
|
228
|
+
id: col.primaryKey(col.int()),
|
|
229
|
+
title: col.varchar(255),
|
|
230
|
+
userId: col.int(),
|
|
231
|
+
createdAt: col.timestamp(),
|
|
229
232
|
});
|
|
230
233
|
|
|
234
|
+
// Add constraints
|
|
235
|
+
posts.columns.title.notNull = true;
|
|
236
|
+
posts.columns.userId.notNull = true;
|
|
237
|
+
|
|
231
238
|
const users = defineTable('users', {
|
|
232
|
-
id: col.
|
|
233
|
-
name: col.varchar(255)
|
|
234
|
-
email: col.varchar(255)
|
|
235
|
-
}, {
|
|
236
|
-
posts: hasMany(posts, 'userId'),
|
|
239
|
+
id: col.primaryKey(col.int()),
|
|
240
|
+
name: col.varchar(255),
|
|
241
|
+
email: col.varchar(255),
|
|
237
242
|
});
|
|
238
243
|
|
|
244
|
+
// Add relations and constraints
|
|
245
|
+
users.relations = {
|
|
246
|
+
posts: hasMany(posts, 'userId'),
|
|
247
|
+
};
|
|
248
|
+
users.columns.name.notNull = true;
|
|
249
|
+
users.columns.email.unique = true;
|
|
250
|
+
|
|
239
251
|
// Build a query with relation & window function
|
|
240
252
|
const builder = new SelectQueryBuilder(users)
|
|
241
253
|
.select({
|
|
@@ -289,36 +301,23 @@ When you're ready, you can let MetalORM manage entities and relations for you.
|
|
|
289
301
|
Instead of “naked objects”, your queries can return entities attached to an `OrmContext`:
|
|
290
302
|
|
|
291
303
|
```ts
|
|
304
|
+
import mysql from 'mysql2/promise';
|
|
292
305
|
import {
|
|
293
306
|
OrmContext,
|
|
294
307
|
MySqlDialect,
|
|
295
308
|
SelectQueryBuilder,
|
|
296
309
|
eq,
|
|
310
|
+
createMysqlExecutor,
|
|
297
311
|
} from 'metal-orm';
|
|
298
312
|
|
|
299
313
|
// 1) Create an OrmContext for this request
|
|
314
|
+
|
|
315
|
+
const connection = await mysql.createConnection({ /* ... */ });
|
|
316
|
+
const executor = createMysqlExecutor(connection);
|
|
317
|
+
|
|
300
318
|
const ctx = new OrmContext({
|
|
301
319
|
dialect: new MySqlDialect(),
|
|
302
|
-
executor
|
|
303
|
-
async executeSql(sql, params) {
|
|
304
|
-
const [rows] = await connection.execute(sql, params);
|
|
305
|
-
// MetalORM expects columns + values; adapt as needed
|
|
306
|
-
return [{
|
|
307
|
-
columns: Object.keys(rows[0] ?? {}),
|
|
308
|
-
values: rows.map(row => Object.values(row)),
|
|
309
|
-
}];
|
|
310
|
-
},
|
|
311
|
-
// Optional: if you want MetalORM to handle transactions around saveChanges()
|
|
312
|
-
async beginTransaction() {
|
|
313
|
-
await connection.beginTransaction();
|
|
314
|
-
},
|
|
315
|
-
async commitTransaction() {
|
|
316
|
-
await connection.commit();
|
|
317
|
-
},
|
|
318
|
-
async rollbackTransaction() {
|
|
319
|
-
await connection.rollback();
|
|
320
|
-
},
|
|
321
|
-
},
|
|
320
|
+
executor,
|
|
322
321
|
});
|
|
323
322
|
|
|
324
323
|
// 2) Load entities with lazy relations
|
|
@@ -382,7 +381,7 @@ class User {
|
|
|
382
381
|
@PrimaryKey(col.int())
|
|
383
382
|
id!: number;
|
|
384
383
|
|
|
385
|
-
@Column(col.varchar(255)
|
|
384
|
+
@Column(col.varchar(255))
|
|
386
385
|
name!: string;
|
|
387
386
|
|
|
388
387
|
@Column(col.varchar(255))
|
|
@@ -400,10 +399,10 @@ class Post {
|
|
|
400
399
|
@PrimaryKey(col.int())
|
|
401
400
|
id!: number;
|
|
402
401
|
|
|
403
|
-
@Column(col.varchar(255)
|
|
402
|
+
@Column(col.varchar(255))
|
|
404
403
|
title!: string;
|
|
405
404
|
|
|
406
|
-
@Column(col.int()
|
|
405
|
+
@Column(col.int())
|
|
407
406
|
userId!: number;
|
|
408
407
|
|
|
409
408
|
@BelongsTo({
|
|
@@ -419,18 +418,11 @@ const tables = bootstrapEntities();
|
|
|
419
418
|
|
|
420
419
|
// 2) Create OrmContext as before
|
|
421
420
|
const connection = await mysql.createConnection({ /* ... */ });
|
|
421
|
+
const executor = createMysqlExecutor(connection);
|
|
422
422
|
|
|
423
423
|
const ctx = new OrmContext({
|
|
424
424
|
dialect: new MySqlDialect(),
|
|
425
|
-
executor
|
|
426
|
-
async executeSql(sql, params) {
|
|
427
|
-
const [rows] = await connection.execute(sql, params);
|
|
428
|
-
return [{
|
|
429
|
-
columns: Object.keys(rows[0] ?? {}),
|
|
430
|
-
values: rows.map(row => Object.values(row)),
|
|
431
|
-
}];
|
|
432
|
-
},
|
|
433
|
-
},
|
|
425
|
+
executor,
|
|
434
426
|
});
|
|
435
427
|
|
|
436
428
|
// 3) Query starting from the entity class
|
|
@@ -477,6 +469,7 @@ Under the hood, MetalORM leans on well-known patterns:
|
|
|
477
469
|
|
|
478
470
|
- **AST + dialect abstraction**: SQL is modeled as typed AST nodes, compiled by dialects that you can extend.
|
|
479
471
|
- **Separation of concerns**: schema, AST, SQL compilation, execution, and ORM runtime are separate layers.
|
|
472
|
+
- **Executor abstraction**: built-in executor creators (`createMysqlExecutor`, `createPostgresExecutor`, etc.) provide a clean separation between database drivers and ORM operations.
|
|
480
473
|
- **Unit of Work + Identity Map**: `OrmContext` coordinates changes and enforces one entity instance per row, following the [Unit of Work](https://en.wikipedia.org/wiki/Unit_of_work) and [Identity map](https://en.wikipedia.org/wiki/Identity_map_pattern) patterns.
|
|
481
474
|
- **Domain events + interceptors**: decouple side-effects from persistence and let cross-cutting concerns hook into flush points, similar in spirit to domain events in [Domain-driven design](https://en.wikipedia.org/wiki/Domain-driven_design).
|
|
482
475
|
|
|
@@ -496,4 +489,4 @@ See the contributing guide for details.
|
|
|
496
489
|
<a id="license"></a>
|
|
497
490
|
## License 📄
|
|
498
491
|
|
|
499
|
-
MetalORM is MIT licensed.
|
|
492
|
+
MetalORM is MIT licensed.
|