@axiosleo/orm-mysql 0.14.4 → 0.15.1
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 +57 -416
- package/bin/orm-mysql.js +1 -1
- package/commands/skills.js +223 -0
- package/package.json +1 -1
- package/skills/SKILL.md +159 -0
- package/skills/crud-operations.md +225 -0
- package/skills/pagination.md +301 -0
- package/skills/query-building.md +159 -0
- package/skills/transactions.md +171 -0
- package/skills/where-conditions.md +190 -0
package/README.md
CHANGED
|
@@ -15,48 +15,8 @@ npm install @axiosleo/orm-mysql
|
|
|
15
15
|
|
|
16
16
|
## Usage
|
|
17
17
|
|
|
18
|
-
### Create MySQL client
|
|
19
|
-
|
|
20
|
-
```javascript
|
|
21
|
-
const { createClient } = require("@axiosleo/orm-mysql");
|
|
22
|
-
|
|
23
|
-
const client = createClient({
|
|
24
|
-
host: process.env.MYSQL_HOST,
|
|
25
|
-
port: process.env.MYSQL_PORT,
|
|
26
|
-
user: process.env.MYSQL_USER,
|
|
27
|
-
password: process.env.MYSQL_PASS,
|
|
28
|
-
database: process.env.MYSQL_DB,
|
|
29
|
-
});
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Initialize database handler
|
|
33
|
-
|
|
34
|
-
```javascript
|
|
35
|
-
const { QueryHandler } = require("@axiosleo/orm-mysql");
|
|
36
|
-
|
|
37
|
-
const db = new QueryHandler(client);
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Initialize query
|
|
41
|
-
|
|
42
|
-
```javascript
|
|
43
|
-
const query = db.table('<table-name>');
|
|
44
|
-
|
|
45
|
-
query.attr("id", "name", "age"); // set attributes
|
|
46
|
-
query.where("name", "Joe"); // set where condition
|
|
47
|
-
query.orWhere("age", ">", 18); // set or where condition
|
|
48
|
-
query.andWhere("age", "<", 30); // set and where condition
|
|
49
|
-
query.orderBy("age", "desc"); // set order by
|
|
50
|
-
query.limit(10); // set limit
|
|
51
|
-
query.offset(0); // set offset
|
|
52
|
-
|
|
53
|
-
let rows = await query.select(); // select
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Some Query Examples
|
|
57
|
-
|
|
58
18
|
```javascript
|
|
59
|
-
const { createClient, QueryHandler
|
|
19
|
+
const { createClient, QueryHandler } = require("@axiosleo/orm-mysql");
|
|
60
20
|
|
|
61
21
|
const conn = createClient({
|
|
62
22
|
host: process.env.MYSQL_HOST,
|
|
@@ -66,415 +26,96 @@ const conn = createClient({
|
|
|
66
26
|
database: process.env.MYSQL_DB,
|
|
67
27
|
});
|
|
68
28
|
|
|
69
|
-
const
|
|
29
|
+
const db = new QueryHandler(conn);
|
|
70
30
|
|
|
71
|
-
|
|
72
|
-
|
|
31
|
+
// SELECT
|
|
32
|
+
const users = await db.table("users")
|
|
33
|
+
.where("age", ">", 18)
|
|
34
|
+
.orderBy("name", "asc")
|
|
35
|
+
.limit(10)
|
|
36
|
+
.select("id", "name", "age");
|
|
73
37
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
query.orWhere("age", ">", 18); // set or where condition
|
|
77
|
-
query.andWhere("age", "<", 30); // set and where condition
|
|
78
|
-
query.orderBy("age", "desc"); // set order by
|
|
79
|
-
query.limit(10); // set limit
|
|
80
|
-
query.offset(0); // set offset
|
|
38
|
+
// FIND single row
|
|
39
|
+
const user = await db.table("users").where("id", 1).find();
|
|
81
40
|
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function findExample() {
|
|
86
|
-
const query = handler.table("users"); // init QueryOperator by table name
|
|
41
|
+
// INSERT
|
|
42
|
+
await db.table("users").insert({ name: "Joe", age: 18 });
|
|
87
43
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
query.orWhere("age", ">", 18); // set or where condition
|
|
91
|
-
query.andWhere("age", "<", 30); // set and where condition
|
|
92
|
-
query.orderBy("age", "desc"); // set order by
|
|
93
|
-
// query.limit(10); // not supported set limit
|
|
94
|
-
// query.offset(10); // not supported set offset
|
|
95
|
-
|
|
96
|
-
let row = await query.find(); // find single row
|
|
97
|
-
}
|
|
44
|
+
// UPDATE
|
|
45
|
+
await db.table("users").where("id", 1).update({ name: "Joe", age: 19 });
|
|
98
46
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// insert
|
|
103
|
-
let row = await query.insert({
|
|
104
|
-
name: "Joe",
|
|
105
|
-
age: 18,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// The insert operation will be changed to the update operation if the uuid already exists
|
|
109
|
-
row = await query.keys('uuid').insert({
|
|
110
|
-
uuid: 'uuid-string', // uuid is unique index
|
|
111
|
-
name: "Joe",
|
|
112
|
-
age: 18,
|
|
113
|
-
})
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function updateExample() {
|
|
117
|
-
const query = handler.table("users");
|
|
118
|
-
|
|
119
|
-
// update
|
|
120
|
-
let row = await query.where("name", "Joe").update({
|
|
121
|
-
name: "Joe",
|
|
122
|
-
age: 18,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// incrBy with number
|
|
126
|
-
row = await query.where("name", "Joe").incrBy("age", 1);
|
|
127
|
-
|
|
128
|
-
// incrBy with string
|
|
129
|
-
row = await query.where("name", "Joe").incrBy("age", "1");
|
|
130
|
-
|
|
131
|
-
// incrBy with Callback
|
|
132
|
-
let result = { status: "success" };
|
|
133
|
-
row = await query.where("id", 1).incrBy("error_times", () => {
|
|
134
|
-
// increase error_times if result.status is not success
|
|
135
|
-
if (result.status !== "success") {
|
|
136
|
-
return 1;
|
|
137
|
-
}
|
|
138
|
-
return 0;
|
|
139
|
-
});
|
|
140
|
-
}
|
|
47
|
+
// DELETE
|
|
48
|
+
await db.table("users").where("id", 1).delete();
|
|
141
49
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// delete with conditions
|
|
146
|
-
let result = await query.where("name", "Joe").delete();
|
|
147
|
-
|
|
148
|
-
// delete by id
|
|
149
|
-
result = await query.delete(1);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async function subqueryExample() {
|
|
153
|
-
const query = handler.table("users", "u");
|
|
154
|
-
const subQuery = new Query("select");
|
|
155
|
-
subQuery.table("users").having("COUNT(*)", ">", 1);
|
|
156
|
-
|
|
157
|
-
const sql = query.where("u.name", subQuery, "IN").buildSql("select").sql;
|
|
158
|
-
// SELECT * FROM `users` AS `u` WHERE `u`.`name` IN (SELECT * FROM `users` GROUP BY `u`.`name` HAVING COUNT(*) > ?)
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Hook
|
|
163
|
-
|
|
164
|
-
```javascript
|
|
165
|
-
const { Hook } = require("@axiosleo/orm-mysql");
|
|
166
|
-
|
|
167
|
-
// opt: 'select' | 'find' | 'insert' | 'update' | 'delete' | 'count'
|
|
168
|
-
|
|
169
|
-
Hook.pre(async (options) => {
|
|
170
|
-
debug.log('options', options);
|
|
171
|
-
}, { table: 'table_name', opt: 'insert'});
|
|
172
|
-
|
|
173
|
-
Hook.post(async (options, result) => {
|
|
174
|
-
throw new Error('some error');
|
|
175
|
-
}, { table: 'table_name', opt: 'insert' });
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Transaction
|
|
179
|
-
|
|
180
|
-
#### Method 1: Using Connection Pool (Recommended)
|
|
181
|
-
|
|
182
|
-
```javascript
|
|
183
|
-
const mysql = require("mysql2");
|
|
184
|
-
const { QueryHandler } = require("@axiosleo/orm-mysql");
|
|
185
|
-
|
|
186
|
-
// Create connection pool
|
|
187
|
-
const pool = mysql.createPool({
|
|
188
|
-
host: process.env.MYSQL_HOST,
|
|
189
|
-
port: process.env.MYSQL_PORT,
|
|
190
|
-
user: process.env.MYSQL_USER,
|
|
191
|
-
password: process.env.MYSQL_PASS,
|
|
192
|
-
database: process.env.MYSQL_DB,
|
|
193
|
-
connectionLimit: 10
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
const handler = new QueryHandler(pool);
|
|
197
|
-
|
|
198
|
-
// Begin transaction - automatically gets a connection from pool
|
|
199
|
-
const transaction = await handler.beginTransaction({
|
|
200
|
-
level: "RC" // READ COMMITTED
|
|
201
|
-
});
|
|
50
|
+
// COUNT
|
|
51
|
+
const total = await db.table("users").where("age", ">", 18).count();
|
|
202
52
|
|
|
53
|
+
// TRANSACTION
|
|
54
|
+
const tx = await db.beginTransaction({ level: "RC" });
|
|
203
55
|
try {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
name: "Joe",
|
|
207
|
-
age: 18,
|
|
208
|
-
});
|
|
209
|
-
const lastInsertId = row.insertId;
|
|
210
|
-
|
|
211
|
-
// Insert student info
|
|
212
|
-
await transaction.table("students").insert({
|
|
213
|
-
user_id: lastInsertId,
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// Commit transaction - connection automatically released back to pool
|
|
217
|
-
await transaction.commit();
|
|
56
|
+
await tx.table("users").insert({ name: "Joe", age: 18 });
|
|
57
|
+
await tx.commit();
|
|
218
58
|
} catch (e) {
|
|
219
|
-
|
|
220
|
-
await transaction.rollback();
|
|
59
|
+
await tx.rollback();
|
|
221
60
|
throw e;
|
|
222
61
|
}
|
|
223
62
|
```
|
|
224
63
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
```javascript
|
|
228
|
-
const { TransactionHandler, createPromiseClient } = require("@axiosleo/orm-mysql");
|
|
64
|
+
> For complete API documentation including where conditions, joins, hooks, migrations, and more, install the [AI Skills](#ai-skills) for your coding assistant, or browse the skill files in [`skills/`](skills/).
|
|
229
65
|
|
|
230
|
-
|
|
231
|
-
host: process.env.MYSQL_HOST,
|
|
232
|
-
port: process.env.MYSQL_PORT,
|
|
233
|
-
user: process.env.MYSQL_USER,
|
|
234
|
-
password: process.env.MYSQL_PASS,
|
|
235
|
-
database: process.env.MYSQL_DB,
|
|
236
|
-
});
|
|
66
|
+
### Migration
|
|
237
67
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
- 'READ UNCOMMITTED' | 'RU' : Lowest isolation, may read dirty data
|
|
242
|
-
- 'READ COMMITTED' | 'RC' : Prevents dirty reads
|
|
243
|
-
- 'REPEATABLE READ' | 'RR' : MySQL default, prevents non-repeatable reads
|
|
244
|
-
- 'SERIALIZABLE' | 'S' : Highest isolation, full serialization
|
|
245
|
-
*/
|
|
246
|
-
level: "SERIALIZABLE", // 'SERIALIZABLE' as default value
|
|
247
|
-
});
|
|
248
|
-
await transaction.begin();
|
|
68
|
+
```bash
|
|
69
|
+
# Generate migration script
|
|
70
|
+
npx orm-mysql generate <name> [dir]
|
|
249
71
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
let row = await transaction.table("users").insert({
|
|
253
|
-
name: "Joe",
|
|
254
|
-
age: 18,
|
|
255
|
-
});
|
|
256
|
-
const lastInsertId = row[0].insertId;
|
|
257
|
-
|
|
258
|
-
// Insert student info
|
|
259
|
-
await transaction.table("students").insert({
|
|
260
|
-
user_id: lastInsertId,
|
|
261
|
-
});
|
|
262
|
-
await transaction.commit();
|
|
263
|
-
} catch (e) {
|
|
264
|
-
await transaction.rollback();
|
|
265
|
-
throw e;
|
|
266
|
-
}
|
|
72
|
+
# Run migration
|
|
73
|
+
npx orm-mysql migrate up [dir] --host=localhost --port=3306 --user=root --pass=pwd --db=mydb
|
|
267
74
|
```
|
|
268
75
|
|
|
269
|
-
|
|
76
|
+
> [Migration examples](./examples/migration/)
|
|
270
77
|
|
|
271
|
-
|
|
272
|
-
const transaction = await handler.beginTransaction({ level: "RC" });
|
|
78
|
+
## AI Skills
|
|
273
79
|
|
|
274
|
-
|
|
275
|
-
// Lock rows for update
|
|
276
|
-
const product = await transaction.table("products")
|
|
277
|
-
.where("sku", "LAPTOP-001")
|
|
278
|
-
.append("FOR UPDATE") // Lock the row
|
|
279
|
-
.find();
|
|
280
|
-
|
|
281
|
-
if (product.stock < 1) {
|
|
282
|
-
throw new Error("Out of stock");
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Update stock
|
|
286
|
-
await transaction.table("products")
|
|
287
|
-
.where("sku", "LAPTOP-001")
|
|
288
|
-
.update({ stock: product.stock - 1 });
|
|
289
|
-
|
|
290
|
-
// Create order
|
|
291
|
-
await transaction.table("orders").insert({
|
|
292
|
-
product_id: product.id,
|
|
293
|
-
quantity: 1,
|
|
294
|
-
total: product.price
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
await transaction.commit();
|
|
298
|
-
} catch (e) {
|
|
299
|
-
await transaction.rollback();
|
|
300
|
-
throw e;
|
|
301
|
-
}
|
|
302
|
-
```
|
|
80
|
+
This project ships with AI Skills that teach AI coding assistants how to use this library correctly. You can install them with a single command:
|
|
303
81
|
|
|
304
|
-
|
|
82
|
+
```bash
|
|
83
|
+
# Cursor
|
|
84
|
+
npx @axiosleo/orm-mysql skills --install=cursor
|
|
305
85
|
|
|
306
|
-
|
|
86
|
+
# Claude Code
|
|
87
|
+
npx @axiosleo/orm-mysql skills --install=claude
|
|
307
88
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const handler = new QueryHandler(pool);
|
|
311
|
-
|
|
312
|
-
// Run 3 transactions concurrently
|
|
313
|
-
await Promise.all([
|
|
314
|
-
(async () => {
|
|
315
|
-
const tx = await handler.beginTransaction();
|
|
316
|
-
try {
|
|
317
|
-
await tx.table("users").insert({ name: "User1" });
|
|
318
|
-
await tx.commit();
|
|
319
|
-
} catch (err) {
|
|
320
|
-
await tx.rollback();
|
|
321
|
-
throw err;
|
|
322
|
-
}
|
|
323
|
-
})(),
|
|
324
|
-
|
|
325
|
-
(async () => {
|
|
326
|
-
const tx = await handler.beginTransaction();
|
|
327
|
-
try {
|
|
328
|
-
await tx.table("users").insert({ name: "User2" });
|
|
329
|
-
await tx.commit();
|
|
330
|
-
} catch (err) {
|
|
331
|
-
await tx.rollback();
|
|
332
|
-
throw err;
|
|
333
|
-
}
|
|
334
|
-
})(),
|
|
335
|
-
|
|
336
|
-
(async () => {
|
|
337
|
-
const tx = await handler.beginTransaction();
|
|
338
|
-
try {
|
|
339
|
-
await tx.table("users").insert({ name: "User3" });
|
|
340
|
-
await tx.commit();
|
|
341
|
-
} catch (err) {
|
|
342
|
-
await tx.rollback();
|
|
343
|
-
throw err;
|
|
344
|
-
}
|
|
345
|
-
})()
|
|
346
|
-
]);
|
|
89
|
+
# Windsurf
|
|
90
|
+
npx @axiosleo/orm-mysql skills --install=windsurf
|
|
347
91
|
```
|
|
348
92
|
|
|
349
|
-
|
|
93
|
+
The command detects the locally installed version of `@axiosleo/orm-mysql` in your `node_modules/` and copies the matching skill files. If the package is not installed locally, it will use the bundled skills and remind you to run `npm install @axiosleo/orm-mysql`.
|
|
350
94
|
|
|
351
|
-
|
|
352
|
-
2. **Choose appropriate isolation level** - Balance between consistency and performance
|
|
353
|
-
3. **Use try-catch-finally** - Ensure transactions are always committed or rolled back
|
|
354
|
-
4. **Keep transactions short** - Avoid long-running operations inside transactions
|
|
355
|
-
5. **Use row locking when needed** - `FOR UPDATE` prevents concurrent modifications
|
|
356
|
-
6. **Handle errors properly** - Always rollback on errors to maintain data consistency
|
|
357
|
-
|
|
358
|
-
### Migration
|
|
359
|
-
|
|
360
|
-
> [Migration examples](./examples/migration/).
|
|
361
|
-
|
|
362
|
-
- Migration script example
|
|
363
|
-
|
|
364
|
-
```javascript
|
|
365
|
-
'use strict';
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* @param {import('@axiosleo/orm-mysql').MigrationInterface} migration
|
|
369
|
-
*/
|
|
370
|
-
function up(migration) {
|
|
371
|
-
migration.createTable('table1', {
|
|
372
|
-
field1: {
|
|
373
|
-
type: 'varchar',
|
|
374
|
-
length: 64,
|
|
375
|
-
allowNull: false,
|
|
376
|
-
uniqIndex: true
|
|
377
|
-
},
|
|
378
|
-
field2: {
|
|
379
|
-
type: 'VARCHAR',
|
|
380
|
-
allowNull: false
|
|
381
|
-
},
|
|
382
|
-
field3: {
|
|
383
|
-
type: 'VARCHAR',
|
|
384
|
-
comment: 'comment',
|
|
385
|
-
allowNull: false
|
|
386
|
-
},
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* @param {import('@axiosleo/orm-mysql').MigrationInterface} migration
|
|
392
|
-
*/
|
|
393
|
-
function down(migration) {
|
|
394
|
-
migration.dropTable('table1');
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
module.exports = {
|
|
398
|
-
up,
|
|
399
|
-
down
|
|
400
|
-
};
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
- Generate migration script
|
|
95
|
+
To uninstall:
|
|
404
96
|
|
|
405
97
|
```bash
|
|
406
|
-
orm-mysql
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
generate [--] [name] <dir>
|
|
411
|
-
gen
|
|
412
|
-
|
|
413
|
-
Arguments:
|
|
414
|
-
|
|
415
|
-
*name Migration name
|
|
416
|
-
dir Migration scripts directory
|
|
98
|
+
npx @axiosleo/orm-mysql skills --uninstall=cursor
|
|
99
|
+
npx @axiosleo/orm-mysql skills --uninstall=claude
|
|
100
|
+
npx @axiosleo/orm-mysql skills --uninstall=windsurf
|
|
417
101
|
```
|
|
418
102
|
|
|
419
|
-
|
|
103
|
+
### Skill Files
|
|
420
104
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
Usage:
|
|
105
|
+
| File | Content |
|
|
106
|
+
|------|---------|
|
|
107
|
+
| `SKILL.md` | Setup, class hierarchy, quick start |
|
|
108
|
+
| `query-building.md` | table, attr, join, orderBy, limit, groupBy |
|
|
109
|
+
| `where-conditions.md` | where, whereIn, whereLike, whereBetween |
|
|
110
|
+
| `crud-operations.md` | select, find, insert, update, delete, incrBy |
|
|
111
|
+
| `transactions.md` | beginTransaction, isolation levels, row locking |
|
|
429
112
|
|
|
430
|
-
|
|
113
|
+
### Manual Installation
|
|
431
114
|
|
|
432
|
-
|
|
115
|
+
For other AI tools, copy the skill files from `node_modules/@axiosleo/orm-mysql/skills/` into your tool's custom instructions directory:
|
|
433
116
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
--port [3306] port number to connect to the database
|
|
437
|
-
--user [root] username for connect to the database
|
|
438
|
-
--pass password to connect to the database
|
|
439
|
-
--db database name
|
|
440
|
-
|
|
441
|
-
Arguments:
|
|
442
|
-
|
|
443
|
-
*action up or down
|
|
444
|
-
dir migration directory
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### Custom query driver
|
|
448
|
-
|
|
449
|
-
```javascript
|
|
450
|
-
const {
|
|
451
|
-
createClient,
|
|
452
|
-
QueryHandler,
|
|
453
|
-
Query,
|
|
454
|
-
Builder
|
|
455
|
-
} = require("@axiosleo/orm-mysql");
|
|
456
|
-
|
|
457
|
-
const conn = createClient({
|
|
458
|
-
host: process.env.MYSQL_HOST,
|
|
459
|
-
port: process.env.MYSQL_PORT,
|
|
460
|
-
user: process.env.MYSQL_USER,
|
|
461
|
-
password: process.env.MYSQL_PASS,
|
|
462
|
-
database: process.env.MYSQL_DB,
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
const hanlder = new QueryHandler(conn, {
|
|
466
|
-
driver: 'custom',
|
|
467
|
-
queryHandler: (con, options) => {
|
|
468
|
-
const builder = new Builder(options);
|
|
469
|
-
return new Promise((resolve, reject) => {
|
|
470
|
-
if (options.operator === 'select') {
|
|
471
|
-
resolve([{ a: 1, b: 2 }]);
|
|
472
|
-
} else {
|
|
473
|
-
reject(new Error('some error'));
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
});
|
|
117
|
+
```bash
|
|
118
|
+
cp -r node_modules/@axiosleo/orm-mysql/skills/ .your-tool/skills/orm-mysql-usage/
|
|
478
119
|
```
|
|
479
120
|
|
|
480
121
|
## License
|