@itrocks/mysql-maintainer 0.2.6 → 0.2.8
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 +61 -241
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,267 +8,87 @@
|
|
|
8
8
|
|
|
9
9
|
Reactively maintains database structure by updating schema and retrying on MySQL errors.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
Connect this package to [`@itrocks/mysql`](https://github.com/itrocks-ts/mysql), then keep using your MySQL data
|
|
12
|
+
sources normally. Their database structure is maintained automatically, as operations need it, when persistent object
|
|
13
|
+
structures evolve. There are no migration files to write and no maintenance API to call.
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Use with the it.rocks framework
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
22
|
-
|
|
23
|
-
`@itrocks/mysql-maintainer` provides two main entry points:
|
|
24
|
-
|
|
25
|
-
- the `Mysql` class, a drop‑in replacement for `@itrocks/mysql` that
|
|
26
|
-
automatically keeps your MySQL/MariaDB schema in sync with your
|
|
27
|
-
TypeScript models while you work with data;
|
|
28
|
-
- the `MysqlMaintainer` class, a lower‑level helper you can call
|
|
29
|
-
directly when you want to create or update tables yourself.
|
|
30
|
-
|
|
31
|
-
In both cases, the package reacts to common MySQL errors
|
|
32
|
-
(`ER_BAD_FIELD_ERROR`, `ER_CANNOT_ADD_FOREIGN`, `ER_NO_SUCH_TABLE`) and
|
|
33
|
-
updates the database structure, then transparently retries the failed
|
|
34
|
-
query.
|
|
35
|
-
|
|
36
|
-
### Minimal example: automatic schema maintenance
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
import { Mysql } from '@itrocks/mysql-maintainer'
|
|
40
|
-
import { Entity } from '@itrocks/storage'
|
|
41
|
-
|
|
42
|
-
class User extends Entity<User>
|
|
43
|
-
{
|
|
44
|
-
name = ''
|
|
45
|
-
email = ''
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function main() {
|
|
49
|
-
const mysql = new Mysql({
|
|
50
|
-
// Same connection options as @itrocks/mysql / mariadb
|
|
51
|
-
host: 'localhost',
|
|
52
|
-
user: 'root',
|
|
53
|
-
database: 'demo',
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
// On first call, mysql-maintainer will create or update the
|
|
57
|
-
// corresponding table based on the User class definition, then retry
|
|
58
|
-
// the insert.
|
|
59
|
-
await mysql.insert(new User().assign({ name: 'Alice', email: 'a@example.com' }))
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
You can continue using the `Mysql` instance as you would use
|
|
64
|
-
`@itrocks/mysql`: methods such as `read`, `search`, `update`, and
|
|
65
|
-
collection helpers are forwarded to the base implementation, while
|
|
66
|
-
`mysql-maintainer` keeps track of the involved entities and adjusts the
|
|
67
|
-
schema when needed.
|
|
68
|
-
|
|
69
|
-
### Example: maintaining join tables on collection access
|
|
70
|
-
|
|
71
|
-
When you read many‑to‑many relationships defined as collections on your
|
|
72
|
-
entities, `Mysql` automatically creates or updates the implicit join
|
|
73
|
-
table.
|
|
17
|
+
An application based on [`@itrocks/framework`](https://github.com/itrocks-ts/framework) needs the framework, the MySQL
|
|
18
|
+
data source, and this package:
|
|
74
19
|
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
import { Entity } from '@itrocks/storage'
|
|
78
|
-
|
|
79
|
-
class Tag extends Entity<Tag>
|
|
80
|
-
{
|
|
81
|
-
name = ''
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
class Article extends Entity<Article>
|
|
85
|
-
{
|
|
86
|
-
title = ''
|
|
87
|
-
|
|
88
|
-
// Many-to-many with Tag; the framework will map this to a join table
|
|
89
|
-
tags: Tag[] = []
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function loadArticleTags(id: number) {
|
|
93
|
-
const mysql = new Mysql({ database: 'demo' })
|
|
94
|
-
|
|
95
|
-
const article = await mysql.read(Article, id)
|
|
96
|
-
|
|
97
|
-
// If the join table does not exist yet or is out of date, it will be
|
|
98
|
-
// created/updated before the query is retried.
|
|
99
|
-
const tags = await mysql.readCollection(article, 'tags')
|
|
100
|
-
return tags
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Example: using MysqlMaintainer directly
|
|
105
|
-
|
|
106
|
-
If you want to control when schema updates happen (for instance during a
|
|
107
|
-
deployment step or a migration command), you can work with
|
|
108
|
-
`MysqlMaintainer` and a `mariadb` connection.
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
import { createConnection } from 'mariadb'
|
|
112
|
-
import { MysqlMaintainer } from '@itrocks/mysql-maintainer'
|
|
113
|
-
import { Entity } from '@itrocks/storage'
|
|
114
|
-
|
|
115
|
-
class Product extends Entity<Product>
|
|
116
|
-
{
|
|
117
|
-
name = ''
|
|
118
|
-
price = 0
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function ensureSchema() {
|
|
122
|
-
const connection = await createConnection({ database: 'demo' })
|
|
123
|
-
const schemaManager = new MysqlMaintainer(connection)
|
|
124
|
-
|
|
125
|
-
// Create or update the table corresponding to Product
|
|
126
|
-
await schemaManager.updateTable(Product)
|
|
127
|
-
|
|
128
|
-
await connection.end()
|
|
129
|
-
}
|
|
20
|
+
```bash
|
|
21
|
+
npm i @itrocks/framework @itrocks/mysql @itrocks/mysql-maintainer
|
|
130
22
|
```
|
|
131
23
|
|
|
132
|
-
|
|
24
|
+
Add the composition and the main MySQL data source to the project's `config.yaml`:
|
|
133
25
|
|
|
134
|
-
|
|
26
|
+
```yaml
|
|
27
|
+
compose:
|
|
28
|
+
'@itrocks/mysql:Mysql': '@itrocks/mysql-maintainer:Mysql'
|
|
135
29
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
#### Properties
|
|
143
|
-
|
|
144
|
-
- `connection?: ContextualConnection` – the underlying `mariadb`
|
|
145
|
-
connection wrapped with additional context information. You normally
|
|
146
|
-
do not interact with it directly.
|
|
147
|
-
|
|
148
|
-
#### Methods
|
|
149
|
-
|
|
150
|
-
All methods have the same signature and return types as in
|
|
151
|
-
`@itrocks/mysql.Mysql`; the extra behavior is that each call pushes the
|
|
152
|
-
relevant entity type or instance to an internal context stack before the
|
|
153
|
-
query, and pops it afterwards.
|
|
154
|
-
|
|
155
|
-
- `async connect(): Promise<ContextualConnection>`
|
|
156
|
-
|
|
157
|
-
Opens the connection and installs the contextual behavior so that
|
|
158
|
-
errors can trigger schema maintenance.
|
|
159
|
-
|
|
160
|
-
- `async insert<T extends object>(object: T): Promise<Entity<T>>`
|
|
161
|
-
|
|
162
|
-
Inserts a new row for `object`. If a “table does not exist” or
|
|
163
|
-
“column not found / foreign key failed” error occurs, the
|
|
164
|
-
corresponding table (and, for collections, the join table) is created
|
|
165
|
-
or updated and the query is retried.
|
|
166
|
-
|
|
167
|
-
- `async update<T extends object>(object: Entity<T>): Promise<Entity<T>>`
|
|
168
|
-
|
|
169
|
-
Updates an existing row; schema maintenance/automatic retry applies in
|
|
170
|
-
the same way as for `insert`.
|
|
171
|
-
|
|
172
|
-
- `async deleteId<T extends object>(type: ObjectOrType<T>, id: any, property?: KeyOf<Entity<T>>): Promise<void>`
|
|
173
|
-
|
|
174
|
-
Deletes a row by its identifier; the entity `type` is used as context
|
|
175
|
-
for potential schema updates.
|
|
176
|
-
|
|
177
|
-
- `async deleteRelatedId<T extends Entity>(object: T, property: KeyOf<T>, id: Identifier): Promise<void>`
|
|
178
|
-
|
|
179
|
-
Deletes a related entity (for example from a collection association).
|
|
180
|
-
|
|
181
|
-
- `async read<T extends object>(type: Type<T>, id: Identifier): Promise<Entity<T> | undefined>`
|
|
182
|
-
|
|
183
|
-
Reads a single entity instance by identifier.
|
|
184
|
-
|
|
185
|
-
- `async readMultiple<T extends object>(type: Type<T>, ids: Identifier[]): Promise<Entity<T>[]>`
|
|
186
|
-
|
|
187
|
-
Reads several entities by their identifiers.
|
|
188
|
-
|
|
189
|
-
- `async readCollection<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Entity<PT>[]>`
|
|
190
|
-
|
|
191
|
-
Reads a collection relationship (for example `article.tags`). The
|
|
192
|
-
context includes both the owning object and the related type so that
|
|
193
|
-
the implicit join table can be created or updated if necessary.
|
|
194
|
-
|
|
195
|
-
- `async readCollectionIds<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Identifier[]>`
|
|
196
|
-
|
|
197
|
-
Same as `readCollection`, but only retrieves identifiers.
|
|
198
|
-
|
|
199
|
-
- `async search<T extends object>(type: Type<T>, search?: SearchType<T>, options?: Options): Promise<Entity<T>[]>`
|
|
200
|
-
|
|
201
|
-
Searches entities; the entity `type` becomes the context for schema
|
|
202
|
-
maintenance.
|
|
203
|
-
|
|
204
|
-
### `class MysqlMaintainer`
|
|
205
|
-
|
|
206
|
-
Low‑level utility that knows how to translate your TypeScript entity
|
|
207
|
-
definitions (via reflection and schema packages) into MySQL tables and
|
|
208
|
-
how to react to common schema‑related errors.
|
|
209
|
-
|
|
210
|
-
#### Constructor
|
|
211
|
-
|
|
212
|
-
```ts
|
|
213
|
-
const maintainer = new MysqlMaintainer(connection)
|
|
30
|
+
dataSource:
|
|
31
|
+
engine: '@itrocks/mysql'
|
|
32
|
+
host: localhost
|
|
33
|
+
user: application
|
|
34
|
+
password: secret
|
|
35
|
+
database: application
|
|
214
36
|
```
|
|
215
37
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
#### Methods
|
|
38
|
+
The `compose` entry enriches `@itrocks/mysql` with the automatic maintenance behaviour. It uses the standard
|
|
39
|
+
[`@itrocks/compose`](https://github.com/itrocks-ts/compose) mechanism for enriching one module with another module's
|
|
40
|
+
features.
|
|
220
41
|
|
|
221
|
-
|
|
42
|
+
The `dataSource` entry above configures the project's main data source. You can instead create other MySQL data sources
|
|
43
|
+
as described in the [`@itrocks/mysql` documentation](https://github.com/itrocks-ts/mysql). Every data source based on
|
|
44
|
+
the composed `@itrocks/mysql` implementation is maintained automatically.
|
|
222
45
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
has been created.
|
|
46
|
+
That is the complete setup. Keep changing and using persistent object classes through the usual it.rocks storage
|
|
47
|
+
operations; the required database changes are applied when an operation first needs them.
|
|
226
48
|
|
|
227
|
-
|
|
49
|
+
## Use outside the it.rocks framework
|
|
228
50
|
|
|
229
|
-
|
|
230
|
-
table name can be resolved for the type.
|
|
51
|
+
The same integration principle can be used without `@itrocks/framework`:
|
|
231
52
|
|
|
232
|
-
|
|
53
|
+
1. Install [`@itrocks/mysql`](https://github.com/itrocks-ts/mysql) and a mixin-based composition system such as
|
|
54
|
+
[`@itrocks/compose`](https://github.com/itrocks-ts/compose). A wrapper implementing the same API as `@itrocks/mysql`
|
|
55
|
+
can be used instead.
|
|
56
|
+
2. Create the required `@itrocks/mysql` data sources as described in its
|
|
57
|
+
[documentation](https://github.com/itrocks-ts/mysql). This package does not change how data sources are configured.
|
|
58
|
+
3. Install `@itrocks/mysql-maintainer` in the project.
|
|
59
|
+
4. Use the chosen composition system to enrich the `Mysql` implementation from `@itrocks/mysql` with the behaviour
|
|
60
|
+
supplied by `@itrocks/mysql-maintainer`. Refer to the
|
|
61
|
+
[`@itrocks/compose` documentation](https://github.com/itrocks-ts/compose) when using that package.
|
|
62
|
+
5. Use the MySQL data sources normally. Their operations now trigger automatic maintenance when the current database
|
|
63
|
+
structure no longer supports the persistent object model.
|
|
233
64
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
the types is not mapped to a table.
|
|
65
|
+
The application integrator is responsible for performing the composition early enough and according to the chosen
|
|
66
|
+
composition system's loading rules.
|
|
237
67
|
|
|
238
|
-
|
|
68
|
+
## How it works
|
|
239
69
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
has been handled and the caller should retry the query, `false`
|
|
244
|
-
otherwise.
|
|
70
|
+
The package enriches the usual `Mysql` operations with the context required to identify the persistent classes involved.
|
|
71
|
+
It intercepts MySQL errors that indicate a missing or outdated table, column, or foreign key, derives the expected
|
|
72
|
+
schema from the object model, applies the required structural changes, and retries the original operation.
|
|
245
73
|
|
|
246
|
-
|
|
74
|
+
For example, a read follows this simplified sequence:
|
|
247
75
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
76
|
+
1. The application reads an object through a MySQL data source as usual.
|
|
77
|
+
2. The enriched data source records the class being read and executes the SQL query.
|
|
78
|
+
3. If MySQL reports a supported structure error, the maintainer compares the database with the schema derived from
|
|
79
|
+
that class and updates the affected table or relationship table.
|
|
80
|
+
4. The data source retries the same query and returns the read result to the application.
|
|
253
81
|
|
|
254
|
-
|
|
82
|
+
The exported classes that implement context tracking, error handling, schema updates, and retries are internal
|
|
83
|
+
mechanics of this integration. They are not a separate API that an application needs to call.
|
|
255
84
|
|
|
256
|
-
|
|
257
|
-
that should exist according to the entity `type`, then applies the
|
|
258
|
-
required SQL changes. Returns `true` when a change has been applied,
|
|
259
|
-
`false` when the schema was already up to date.
|
|
85
|
+
## Persistent model requirements
|
|
260
86
|
|
|
261
|
-
|
|
87
|
+
Automatic maintenance derives database structure from business classes and their reflection metadata. Those classes
|
|
88
|
+
must follow the it.rocks persistent object conventions, including the decorators and metadata used by its ORM packages.
|
|
262
89
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
- Applications with many small deployments where you prefer incremental
|
|
269
|
-
schema evolution handled by the application itself.
|
|
270
|
-
- Projects using the `@itrocks/storage` / `@itrocks/mysql` stack that
|
|
271
|
-
want schema maintenance without writing migration scripts.
|
|
272
|
-
- Custom migration tools or CLI scripts built on top of
|
|
273
|
-
`MysqlMaintainer` to validate and update schema before a new version
|
|
274
|
-
of the application is started.
|
|
90
|
+
The current implementation is strongly coupled to the it.rocks ORM ecosystem. Its
|
|
91
|
+
[`npm dependencies`](https://www.npmjs.com/package/@itrocks/mysql-maintainer?activeTab=dependencies) show the
|
|
92
|
+
reflection, storage, schema conversion, and schema diff modules involved. Outside a full it.rocks application, the
|
|
93
|
+
integrator must provide a compatible model and metadata environment in addition to the MySQL and composition
|
|
94
|
+
integrations described above.
|
package/package.json
CHANGED