metal-orm 1.0.4 → 1.0.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/README.md +299 -113
- package/docs/CHANGES.md +104 -0
- package/docs/advanced-features.md +92 -1
- package/docs/api-reference.md +13 -4
- package/docs/dml-operations.md +156 -0
- package/docs/getting-started.md +122 -55
- package/docs/hydration.md +77 -3
- package/docs/index.md +19 -14
- package/docs/multi-dialect-support.md +25 -0
- package/docs/query-builder.md +60 -0
- package/docs/runtime.md +105 -0
- package/docs/schema-definition.md +52 -1
- package/package.json +1 -1
- package/src/ast/expression.ts +630 -592
- package/src/ast/query.ts +110 -49
- package/src/builder/delete-query-state.ts +42 -0
- package/src/builder/delete.ts +57 -0
- package/src/builder/hydration-manager.ts +3 -2
- package/src/builder/hydration-planner.ts +163 -107
- package/src/builder/insert-query-state.ts +62 -0
- package/src/builder/insert.ts +59 -0
- package/src/builder/operations/relation-manager.ts +1 -23
- package/src/builder/relation-conditions.ts +45 -1
- package/src/builder/relation-service.ts +81 -18
- package/src/builder/relation-types.ts +15 -0
- package/src/builder/relation-utils.ts +12 -0
- package/src/builder/select.ts +427 -394
- package/src/builder/update-query-state.ts +59 -0
- package/src/builder/update.ts +61 -0
- package/src/constants/sql-operator-config.ts +3 -0
- package/src/constants/sql.ts +38 -32
- package/src/dialect/abstract.ts +107 -47
- package/src/dialect/mssql/index.ts +31 -6
- package/src/dialect/mysql/index.ts +31 -6
- package/src/dialect/postgres/index.ts +45 -6
- package/src/dialect/sqlite/index.ts +45 -6
- package/src/index.ts +22 -11
- package/src/playground/features/playground/data/scenarios/hydration.ts +23 -11
- package/src/playground/features/playground/data/scenarios/types.ts +18 -15
- package/src/playground/features/playground/data/schema.ts +6 -2
- package/src/playground/features/playground/services/QueryExecutionService.ts +2 -1
- package/src/runtime/entity-meta.ts +52 -0
- package/src/runtime/entity.ts +252 -0
- package/src/runtime/execute.ts +36 -0
- package/src/runtime/hydration.ts +100 -38
- package/src/runtime/lazy-batch.ts +205 -0
- package/src/runtime/orm-context.ts +539 -0
- package/src/runtime/relations/belongs-to.ts +92 -0
- package/src/runtime/relations/has-many.ts +111 -0
- package/src/runtime/relations/many-to-many.ts +149 -0
- package/src/schema/column.ts +15 -1
- package/src/schema/relation.ts +105 -40
- package/src/schema/table.ts +34 -22
- package/src/schema/types.ts +76 -0
- package/tests/belongs-to-many.test.ts +57 -0
- package/tests/dml.test.ts +206 -0
- package/tests/orm-runtime.test.ts +254 -0
package/docs/runtime.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Runtime & Unit of Work
|
|
2
|
+
|
|
3
|
+
This page describes MetalORM's optional entity runtime:
|
|
4
|
+
|
|
5
|
+
- `OrmContext` – the Unit of Work.
|
|
6
|
+
- entities – proxies wrapping hydrated rows.
|
|
7
|
+
- relation wrappers – lazy, batched collections and references.
|
|
8
|
+
|
|
9
|
+
## OrmContext
|
|
10
|
+
|
|
11
|
+
`OrmContext` owns:
|
|
12
|
+
|
|
13
|
+
- a SQL dialect,
|
|
14
|
+
- a DB executor (`executeSql(sql, params)`),
|
|
15
|
+
- an identity map (`table + primaryKey → entity`),
|
|
16
|
+
- change tracking for entities and relations,
|
|
17
|
+
- hooks and (optionally) domain event dispatch.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
const ctx = new OrmContext({
|
|
21
|
+
dialect: new MySqlDialect(),
|
|
22
|
+
db: {
|
|
23
|
+
async executeSql(sql, params) {
|
|
24
|
+
// call your DB driver here
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Entities
|
|
31
|
+
|
|
32
|
+
Entities are created when you call `.execute(ctx)` on a SelectQueryBuilder.
|
|
33
|
+
|
|
34
|
+
They:
|
|
35
|
+
|
|
36
|
+
- expose table columns as properties (user.id, user.name, …)
|
|
37
|
+
- expose relations as wrappers:
|
|
38
|
+
- HasManyCollection<T> (e.g. user.posts)
|
|
39
|
+
- BelongsToReference<T> (e.g. post.author)
|
|
40
|
+
- ManyToManyCollection<T> (e.g. user.roles)
|
|
41
|
+
- track changes to fields and collections for the Unit of Work.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const [user] = await new SelectQueryBuilder(users)
|
|
45
|
+
.select({ id: users.columns.id, name: users.columns.name })
|
|
46
|
+
.includeLazy('posts')
|
|
47
|
+
.execute(ctx);
|
|
48
|
+
|
|
49
|
+
user.name = 'Updated Name'; // marks entity as Dirty
|
|
50
|
+
const posts = await user.posts.load(); // lazy-batched load
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Unit of Work
|
|
54
|
+
|
|
55
|
+
Each entity in an OrmContext has a status:
|
|
56
|
+
|
|
57
|
+
- New – created in memory and not yet persisted.
|
|
58
|
+
- Managed – loaded from the database and unchanged.
|
|
59
|
+
- Dirty – modified scalar properties.
|
|
60
|
+
- Removed – scheduled for deletion.
|
|
61
|
+
|
|
62
|
+
Relations track:
|
|
63
|
+
|
|
64
|
+
- additions (add, attach, syncByIds),
|
|
65
|
+
- removals (remove, detach).
|
|
66
|
+
|
|
67
|
+
`ctx.saveChanges()`:
|
|
68
|
+
|
|
69
|
+
- runs hooks / interceptors,
|
|
70
|
+
- flushes entity changes as INSERT / UPDATE / DELETE,
|
|
71
|
+
- flushes relation changes (FK / pivot),
|
|
72
|
+
- dispatches domain events (optional),
|
|
73
|
+
- resets tracking.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
user.posts.add({ title: 'From entities' });
|
|
77
|
+
user.posts.remove(posts[0]);
|
|
78
|
+
|
|
79
|
+
await ctx.saveChanges();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Hooks & Domain Events
|
|
83
|
+
|
|
84
|
+
Each TableDef can define hooks:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const users = defineTable('users', { /* ... */ }, undefined, {
|
|
88
|
+
hooks: {
|
|
89
|
+
beforeInsert(ctx, user) {
|
|
90
|
+
user.createdAt = new Date();
|
|
91
|
+
},
|
|
92
|
+
afterUpdate(ctx, user) {
|
|
93
|
+
// log audit event
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Entities may accumulate domain events:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
addDomainEvent(user, new UserRegisteredEvent(user.id));
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
After flushing, the context dispatches these events to registered handlers or writes them to an outbox table.
|
|
@@ -37,7 +37,9 @@ You can also chain modifiers to define column constraints:
|
|
|
37
37
|
|
|
38
38
|
## Relations
|
|
39
39
|
|
|
40
|
-
You can define relations between tables using `hasMany` and `
|
|
40
|
+
You can define relations between tables using `hasMany`, `belongsTo`, and `belongsToMany`:
|
|
41
|
+
|
|
42
|
+
### One-to-Many Relations
|
|
41
43
|
|
|
42
44
|
```typescript
|
|
43
45
|
import { defineTable, col, hasMany } from 'metal-orm';
|
|
@@ -59,3 +61,52 @@ const users = defineTable(
|
|
|
59
61
|
}
|
|
60
62
|
);
|
|
61
63
|
```
|
|
64
|
+
|
|
65
|
+
### Many-to-One Relations
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const posts = defineTable('posts', {
|
|
69
|
+
id: col.int().primaryKey(),
|
|
70
|
+
title: col.varchar(255).notNull(),
|
|
71
|
+
userId: col.int().notNull(),
|
|
72
|
+
}, {
|
|
73
|
+
author: belongsTo(users, 'userId')
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Many-to-Many Relations
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
const projects = defineTable('projects', {
|
|
81
|
+
id: col.int().primaryKey(),
|
|
82
|
+
name: col.varchar(255).notNull(),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const projectAssignments = defineTable('project_assignments', {
|
|
86
|
+
id: col.int().primaryKey(),
|
|
87
|
+
userId: col.int().notNull(),
|
|
88
|
+
projectId: col.int().notNull(),
|
|
89
|
+
role: col.varchar(50),
|
|
90
|
+
assignedAt: col.timestamp(),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const users = defineTable('users', {
|
|
94
|
+
id: col.int().primaryKey(),
|
|
95
|
+
name: col.varchar(255).notNull(),
|
|
96
|
+
}, {
|
|
97
|
+
projects: belongsToMany(
|
|
98
|
+
projects,
|
|
99
|
+
projectAssignments,
|
|
100
|
+
{
|
|
101
|
+
pivotForeignKeyToRoot: 'userId',
|
|
102
|
+
pivotForeignKeyToTarget: 'projectId',
|
|
103
|
+
defaultPivotColumns: ['role', 'assignedAt']
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
> **Note**: When using the runtime, relation definitions (`hasMany`, `belongsTo`, `belongsToMany`) are also used to:
|
|
109
|
+
> - generate hydration plans for eager loading
|
|
110
|
+
> - configure lazy relation loaders
|
|
111
|
+
> - control cascade behavior in `OrmContext.saveChanges()`.
|
|
112
|
+
```
|