mysql-orm-lite 1.0.0
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/LICENSE +21 -0
- package/README.md +1421 -0
- package/index.js +42 -0
- package/lib/TransactionCRUD.js +208 -0
- package/lib/connectionManager.js +117 -0
- package/lib/crud.js +301 -0
- package/lib/transactionManager.js +21 -0
- package/package.json +41 -0
- package/utils/logger.js +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,1421 @@
|
|
|
1
|
+
# MySQL ORM Lite
|
|
2
|
+
|
|
3
|
+
A lightweight ORM for MySQL with connection pooling, CRUD operations, query builder, and transaction support for Node.js applications.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/mysql-orm-lite)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Why MySQL ORM Lite?
|
|
9
|
+
|
|
10
|
+
- ðŠķ **Lightweight** - Minimal overhead, maximum performance
|
|
11
|
+
- ðŊ **Simple API** - Intuitive methods for common operations
|
|
12
|
+
- ð **Connection Pooling** - Efficient MySQL connection management
|
|
13
|
+
- ðïļ **Query Builder** - Build complex queries with MongoDB-style operators
|
|
14
|
+
- ðū **Transaction Support** - Full ACID transaction capabilities
|
|
15
|
+
- ð **SQL Injection Safe** - Parameterized queries by default
|
|
16
|
+
- ð **Performance Monitoring** - Automatic slow query logging (>1s)
|
|
17
|
+
- ð **Multiple Databases** - Connect to multiple MySQL databases simultaneously
|
|
18
|
+
- ð **Custom Logger** - Integrate with Winston, Bunyan, or any custom logger
|
|
19
|
+
|
|
20
|
+
## Table of Contents
|
|
21
|
+
|
|
22
|
+
- [Installation](#installation)
|
|
23
|
+
- [Quick Start](#quick-start)
|
|
24
|
+
- [API Reference](#api-reference)
|
|
25
|
+
- [Connection Management](#connection-management)
|
|
26
|
+
- [CRUD Operations](#crud-operations)
|
|
27
|
+
- [Query Builder](#query-builder)
|
|
28
|
+
- [WHERE Operators](#where-operators)
|
|
29
|
+
- [JOIN Operations](#join-operations)
|
|
30
|
+
- [Transactions](#transactions)
|
|
31
|
+
- [Transaction Manager](#transaction-manager)
|
|
32
|
+
- [Advanced Usage](#advanced-usage)
|
|
33
|
+
- [Multiple Database Connections](#multiple-database-connections)
|
|
34
|
+
- [Raw SQL Expressions](#raw-sql-expressions)
|
|
35
|
+
- [Custom Logger Integration](#custom-logger-integration)
|
|
36
|
+
- [Error Handling](#error-handling)
|
|
37
|
+
- [Performance Tips](#performance-tips)
|
|
38
|
+
- [Complete Examples](#complete-examples)
|
|
39
|
+
- [API Quick Reference](#api-quick-reference)
|
|
40
|
+
- [License](#license)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install mysql-orm-lite mysql2
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> **Note:** `mysql2` is a peer dependency and must be installed separately.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
const db = require('mysql-orm-lite');
|
|
58
|
+
|
|
59
|
+
// Initialize with your database configuration
|
|
60
|
+
db.connectionManager.init({
|
|
61
|
+
host: 'localhost',
|
|
62
|
+
user: 'root',
|
|
63
|
+
password: 'your_password',
|
|
64
|
+
database: 'your_database',
|
|
65
|
+
port: 3306,
|
|
66
|
+
connectionLimit: 10
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Basic CRUD operations
|
|
70
|
+
async function example() {
|
|
71
|
+
// Insert a record
|
|
72
|
+
const userId = await db.insert('users', {
|
|
73
|
+
name: 'John Doe',
|
|
74
|
+
email: 'john@example.com',
|
|
75
|
+
age: 30
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Find using query builder
|
|
79
|
+
const users = await db.select({
|
|
80
|
+
table: 'users',
|
|
81
|
+
where: { id: userId }
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Update a record
|
|
85
|
+
await db.updateWhere({
|
|
86
|
+
table: 'users',
|
|
87
|
+
data: { name: 'Jane Doe' },
|
|
88
|
+
where: { id: userId }
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Delete a record
|
|
92
|
+
await db.deleteWhere({
|
|
93
|
+
table: 'users',
|
|
94
|
+
where: { id: userId }
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Close connections on app shutdown
|
|
98
|
+
await db.connectionManager.closeAllPools();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
example();
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## API Reference
|
|
107
|
+
|
|
108
|
+
### Connection Management
|
|
109
|
+
|
|
110
|
+
#### `connectionManager.init(config, logger?)`
|
|
111
|
+
|
|
112
|
+
Initialize the connection manager with database configuration.
|
|
113
|
+
|
|
114
|
+
**Parameters:**
|
|
115
|
+
| Parameter | Type | Required | Description |
|
|
116
|
+
|-----------|------|----------|-------------|
|
|
117
|
+
| `config` | Object | Yes | Database configuration object |
|
|
118
|
+
| `logger` | Object | No | Custom logger instance |
|
|
119
|
+
|
|
120
|
+
**Config Options:**
|
|
121
|
+
| Option | Type | Default | Description |
|
|
122
|
+
|--------|------|---------|-------------|
|
|
123
|
+
| `host` | string | - | Database host |
|
|
124
|
+
| `user` | string | - | Database user |
|
|
125
|
+
| `password` | string | - | Database password |
|
|
126
|
+
| `database` | string | - | Database name |
|
|
127
|
+
| `port` | number | 3306 | Database port |
|
|
128
|
+
| `connectionLimit` | number | 10 | Max pool connections |
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
db.connectionManager.init({
|
|
132
|
+
host: 'localhost',
|
|
133
|
+
user: 'root',
|
|
134
|
+
password: 'password',
|
|
135
|
+
database: 'mydb',
|
|
136
|
+
port: 3306,
|
|
137
|
+
connectionLimit: 10
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### `connectionManager.setLogger(customLogger)`
|
|
142
|
+
|
|
143
|
+
Set a custom logger with `info`, `error`, and `warn` methods.
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
db.connectionManager.setLogger({
|
|
147
|
+
info: (...args) => console.log('[DB INFO]', ...args),
|
|
148
|
+
error: (...args) => console.error('[DB ERROR]', ...args),
|
|
149
|
+
warn: (...args) => console.warn('[DB WARN]', ...args)
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### `connectionManager.getPool(dbConfig?)`
|
|
154
|
+
|
|
155
|
+
Get or create a connection pool. Returns a Promise.
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
const pool = await db.connectionManager.getPool();
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### `connectionManager.closePool(config)`
|
|
162
|
+
|
|
163
|
+
Close a specific connection pool.
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
await db.connectionManager.closePool({
|
|
167
|
+
host: 'localhost',
|
|
168
|
+
user: 'root',
|
|
169
|
+
database: 'mydb'
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `connectionManager.closeAllPools()`
|
|
174
|
+
|
|
175
|
+
Close all connection pools. **Call this on application shutdown.**
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
process.on('SIGINT', async () => {
|
|
179
|
+
await db.connectionManager.closeAllPools();
|
|
180
|
+
process.exit(0);
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### `connectionManager.getLogger()`
|
|
185
|
+
|
|
186
|
+
Get the current logger instance.
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
const logger = db.connectionManager.getLogger();
|
|
190
|
+
logger.info('Custom log message');
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
### CRUD Operations
|
|
196
|
+
|
|
197
|
+
#### `insert(table, data, dbConfig?, debug?, isIgnore?)`
|
|
198
|
+
|
|
199
|
+
Insert a record into a table. Returns the `insertId`.
|
|
200
|
+
|
|
201
|
+
**Parameters:**
|
|
202
|
+
| Parameter | Type | Required | Description |
|
|
203
|
+
|-----------|------|----------|-------------|
|
|
204
|
+
| `table` | string | Yes | Table name |
|
|
205
|
+
| `data` | Object | Yes | Key-value pairs to insert |
|
|
206
|
+
| `dbConfig` | Object | No | Database config (uses default if not provided) |
|
|
207
|
+
| `debug` | boolean | No | Log insert details |
|
|
208
|
+
| `isIgnore` | boolean | No | Use INSERT IGNORE |
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
// Basic insert
|
|
212
|
+
const id = await db.insert('users', {
|
|
213
|
+
name: 'John',
|
|
214
|
+
email: 'john@example.com',
|
|
215
|
+
created_at: new Date()
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Insert with debug logging
|
|
219
|
+
const id = await db.insert('users', { name: 'John' }, null, true);
|
|
220
|
+
|
|
221
|
+
// INSERT IGNORE (skip duplicates)
|
|
222
|
+
const id = await db.insert('users', { email: 'john@example.com' }, null, false, true);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### `update(table, data, whereClause, dbConfig?, debug?)`
|
|
226
|
+
|
|
227
|
+
Update records in a table. Returns `affectedRows`.
|
|
228
|
+
|
|
229
|
+
**Parameters:**
|
|
230
|
+
| Parameter | Type | Required | Description |
|
|
231
|
+
|-----------|------|----------|-------------|
|
|
232
|
+
| `table` | string | Yes | Table name |
|
|
233
|
+
| `data` | Object | Yes | Key-value pairs to update |
|
|
234
|
+
| `whereClause` | string | Yes | WHERE clause (must include 'WHERE') |
|
|
235
|
+
| `dbConfig` | Object | No | Database config |
|
|
236
|
+
| `debug` | boolean | No | Log update details |
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
const affectedRows = await db.update(
|
|
240
|
+
'users',
|
|
241
|
+
{ name: 'Jane', updated_at: new Date() },
|
|
242
|
+
'WHERE id = 1'
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// With debug
|
|
246
|
+
const affectedRows = await db.update(
|
|
247
|
+
'users',
|
|
248
|
+
{ status: 'active' },
|
|
249
|
+
'WHERE created_at < NOW()',
|
|
250
|
+
null,
|
|
251
|
+
true
|
|
252
|
+
);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### `delete(whereClause, table, dbConfig?)`
|
|
256
|
+
|
|
257
|
+
Delete records from a table. Returns `affectedRows`.
|
|
258
|
+
|
|
259
|
+
**Parameters:**
|
|
260
|
+
| Parameter | Type | Required | Description |
|
|
261
|
+
|-----------|------|----------|-------------|
|
|
262
|
+
| `whereClause` | string | Yes | WHERE clause (must include 'WHERE') |
|
|
263
|
+
| `table` | string | Yes | Table name |
|
|
264
|
+
| `dbConfig` | Object | No | Database config |
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
const affectedRows = await db.delete('WHERE id = 1', 'users');
|
|
268
|
+
|
|
269
|
+
// Delete with complex condition
|
|
270
|
+
const affectedRows = await db.delete(
|
|
271
|
+
'WHERE status = "deleted" AND deleted_at < DATE_SUB(NOW(), INTERVAL 30 DAY)',
|
|
272
|
+
'users'
|
|
273
|
+
);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### `find(query, params?, dbConfig?)`
|
|
277
|
+
|
|
278
|
+
Execute a raw SELECT query. Returns an array of results.
|
|
279
|
+
|
|
280
|
+
**Parameters:**
|
|
281
|
+
| Parameter | Type | Required | Description |
|
|
282
|
+
|-----------|------|----------|-------------|
|
|
283
|
+
| `query` | string | Yes | SQL query string |
|
|
284
|
+
| `params` | Array | No | Query parameters (for ? placeholders) |
|
|
285
|
+
| `dbConfig` | Object | No | Database config |
|
|
286
|
+
|
|
287
|
+
```javascript
|
|
288
|
+
// Simple query
|
|
289
|
+
const users = await db.find('SELECT * FROM users');
|
|
290
|
+
|
|
291
|
+
// Parameterized query
|
|
292
|
+
const users = await db.find(
|
|
293
|
+
'SELECT * FROM users WHERE age > ? AND status = ?',
|
|
294
|
+
[18, 'active']
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
// Complex query with joins
|
|
298
|
+
const orders = await db.find(`
|
|
299
|
+
SELECT o.*, u.name as user_name
|
|
300
|
+
FROM orders o
|
|
301
|
+
INNER JOIN users u ON o.user_id = u.id
|
|
302
|
+
WHERE o.total > ?
|
|
303
|
+
ORDER BY o.created_at DESC
|
|
304
|
+
LIMIT 10
|
|
305
|
+
`, [100]);
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
#### `findCount(query, params?, dbConfig?)`
|
|
309
|
+
|
|
310
|
+
Execute a COUNT query. Returns the count value.
|
|
311
|
+
|
|
312
|
+
**Parameters:**
|
|
313
|
+
| Parameter | Type | Required | Description |
|
|
314
|
+
|-----------|------|----------|-------------|
|
|
315
|
+
| `query` | string | Yes | COUNT query (must return `count` alias) |
|
|
316
|
+
| `params` | Array | No | Query parameters |
|
|
317
|
+
| `dbConfig` | Object | No | Database config |
|
|
318
|
+
|
|
319
|
+
```javascript
|
|
320
|
+
const count = await db.findCount(
|
|
321
|
+
'SELECT COUNT(*) as count FROM users WHERE age > ?',
|
|
322
|
+
[18]
|
|
323
|
+
);
|
|
324
|
+
console.log(`Found ${count} users`);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
### Query Builder
|
|
330
|
+
|
|
331
|
+
The query builder provides a fluent API to construct SQL queries programmatically. All methods support both verbose and concise naming.
|
|
332
|
+
|
|
333
|
+
#### Method Aliases
|
|
334
|
+
|
|
335
|
+
| Verbose Name | Short Aliases |
|
|
336
|
+
|--------------|---------------|
|
|
337
|
+
| `buildAndExecuteSelectQuery` | `select`, `findWhere`, `query` |
|
|
338
|
+
| `buildAndExecuteUpdateQuery` | `updateWhere`, `updateQuery` |
|
|
339
|
+
| `buildAndExecuteDeleteQuery` | `deleteWhere`, `remove` |
|
|
340
|
+
|
|
341
|
+
#### `select(options, dbConfig?)` / `buildAndExecuteSelectQuery()`
|
|
342
|
+
|
|
343
|
+
Build and execute a SELECT query.
|
|
344
|
+
|
|
345
|
+
**Options:**
|
|
346
|
+
| Option | Type | Description |
|
|
347
|
+
|--------|------|-------------|
|
|
348
|
+
| `table` | string | Table name (required) |
|
|
349
|
+
| `alias` | string | Table alias |
|
|
350
|
+
| `fields` | Array/string | Fields to select (default: '*') |
|
|
351
|
+
| `where` | Object | WHERE conditions |
|
|
352
|
+
| `joins` | Array | JOIN clauses |
|
|
353
|
+
| `orderBy` | string | ORDER BY clause |
|
|
354
|
+
| `groupBy` | string | GROUP BY clause |
|
|
355
|
+
| `having` | string | HAVING clause |
|
|
356
|
+
| `limit` | number | LIMIT value |
|
|
357
|
+
| `offset` | number | OFFSET value |
|
|
358
|
+
|
|
359
|
+
```javascript
|
|
360
|
+
// Simple select
|
|
361
|
+
const users = await db.select({
|
|
362
|
+
table: 'users',
|
|
363
|
+
where: { status: 'active' }
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Select with all options
|
|
367
|
+
const users = await db.select({
|
|
368
|
+
table: 'users',
|
|
369
|
+
alias: 'u',
|
|
370
|
+
fields: ['id', 'name', 'email', 'created_at'],
|
|
371
|
+
where: {
|
|
372
|
+
age: { $gte: 18, $lte: 65 },
|
|
373
|
+
status: 'active'
|
|
374
|
+
},
|
|
375
|
+
orderBy: 'created_at DESC',
|
|
376
|
+
groupBy: 'department_id',
|
|
377
|
+
having: 'COUNT(*) > 5',
|
|
378
|
+
limit: 10,
|
|
379
|
+
offset: 0
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Using aliases
|
|
383
|
+
const result1 = await db.findWhere({ table: 'users', where: { id: 1 } });
|
|
384
|
+
const result2 = await db.query({ table: 'products', limit: 100 });
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
#### `updateWhere(options, dbConfig?)` / `buildAndExecuteUpdateQuery()`
|
|
388
|
+
|
|
389
|
+
Build and execute an UPDATE query. Returns `affectedRows`.
|
|
390
|
+
|
|
391
|
+
**Options:**
|
|
392
|
+
| Option | Type | Description |
|
|
393
|
+
|--------|------|-------------|
|
|
394
|
+
| `table` | string | Table name (required) |
|
|
395
|
+
| `data` | Object | Data to update (required) |
|
|
396
|
+
| `where` | Object | WHERE conditions |
|
|
397
|
+
|
|
398
|
+
```javascript
|
|
399
|
+
// Update with query builder
|
|
400
|
+
const affectedRows = await db.updateWhere({
|
|
401
|
+
table: 'users',
|
|
402
|
+
data: {
|
|
403
|
+
status: 'inactive',
|
|
404
|
+
updated_at: new Date()
|
|
405
|
+
},
|
|
406
|
+
where: {
|
|
407
|
+
last_login: { $lt: '2023-01-01' }
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Update multiple conditions
|
|
412
|
+
await db.updateQuery({
|
|
413
|
+
table: 'products',
|
|
414
|
+
data: { stock: 0 },
|
|
415
|
+
where: {
|
|
416
|
+
$or: [
|
|
417
|
+
{ discontinued: true },
|
|
418
|
+
{ expiry_date: { $lt: new Date() } }
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
#### `deleteWhere(options, dbConfig?)` / `buildAndExecuteDeleteQuery()`
|
|
425
|
+
|
|
426
|
+
Build and execute a DELETE query. Returns `affectedRows`. **Requires a WHERE clause for safety.**
|
|
427
|
+
|
|
428
|
+
**Options:**
|
|
429
|
+
| Option | Type | Description |
|
|
430
|
+
|--------|------|-------------|
|
|
431
|
+
| `table` | string | Table name (required) |
|
|
432
|
+
| `where` | Object | WHERE conditions (required) |
|
|
433
|
+
|
|
434
|
+
```javascript
|
|
435
|
+
// Delete with conditions
|
|
436
|
+
const affectedRows = await db.deleteWhere({
|
|
437
|
+
table: 'sessions',
|
|
438
|
+
where: {
|
|
439
|
+
expired_at: { $lt: new Date() }
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// Using 'remove' alias
|
|
444
|
+
await db.remove({
|
|
445
|
+
table: 'logs',
|
|
446
|
+
where: {
|
|
447
|
+
created_at: { $lt: '2024-01-01' },
|
|
448
|
+
level: { $in: ['debug', 'trace'] }
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
### WHERE Operators
|
|
456
|
+
|
|
457
|
+
The query builder supports MongoDB-style operators for building WHERE clauses.
|
|
458
|
+
|
|
459
|
+
#### Comparison Operators
|
|
460
|
+
|
|
461
|
+
| Operator | SQL Equivalent | Example |
|
|
462
|
+
|----------|----------------|---------|
|
|
463
|
+
| `$eq` | `=` | `{ status: { $eq: 'active' } }` |
|
|
464
|
+
| `$ne` | `!=` | `{ status: { $ne: 'deleted' } }` |
|
|
465
|
+
| `$gt` | `>` | `{ age: { $gt: 18 } }` |
|
|
466
|
+
| `$gte` | `>=` | `{ age: { $gte: 21 } }` |
|
|
467
|
+
| `$lt` | `<` | `{ price: { $lt: 100 } }` |
|
|
468
|
+
| `$lte` | `<=` | `{ quantity: { $lte: 10 } }` |
|
|
469
|
+
|
|
470
|
+
#### Array Operators
|
|
471
|
+
|
|
472
|
+
| Operator | SQL Equivalent | Example |
|
|
473
|
+
|----------|----------------|---------|
|
|
474
|
+
| `$in` | `IN (...)` | `{ status: { $in: ['active', 'pending'] } }` |
|
|
475
|
+
| `$nin` / `$notIn` | `NOT IN (...)` | `{ role: { $nin: ['admin', 'moderator'] } }` |
|
|
476
|
+
|
|
477
|
+
#### Pattern Matching
|
|
478
|
+
|
|
479
|
+
| Operator | SQL Equivalent | Example |
|
|
480
|
+
|----------|----------------|---------|
|
|
481
|
+
| `$like` | `LIKE` | `{ name: { $like: '%John%' } }` |
|
|
482
|
+
| `$between` | `BETWEEN` | `{ age: { $between: [18, 65] } }` |
|
|
483
|
+
|
|
484
|
+
#### Logical Operators
|
|
485
|
+
|
|
486
|
+
| Operator | Description | Example |
|
|
487
|
+
|----------|-------------|---------|
|
|
488
|
+
| `$and` | Logical AND | `{ $and: [{ age: { $gte: 18 } }, { status: 'active' }] }` |
|
|
489
|
+
| `$or` | Logical OR | `{ $or: [{ city: 'NYC' }, { city: 'LA' }] }` |
|
|
490
|
+
| `$not` | Logical NOT | `{ $not: { status: 'deleted' } }` |
|
|
491
|
+
|
|
492
|
+
#### NULL Handling
|
|
493
|
+
|
|
494
|
+
```javascript
|
|
495
|
+
// Check for NULL
|
|
496
|
+
{ deleted_at: null } // WHERE deleted_at IS NULL
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### Complete WHERE Examples
|
|
500
|
+
|
|
501
|
+
```javascript
|
|
502
|
+
// Multiple operators on same field
|
|
503
|
+
await db.select({
|
|
504
|
+
table: 'users',
|
|
505
|
+
where: {
|
|
506
|
+
age: { $gte: 18, $lte: 65 } // age >= 18 AND age <= 65
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
// OR conditions
|
|
511
|
+
await db.select({
|
|
512
|
+
table: 'users',
|
|
513
|
+
where: {
|
|
514
|
+
$or: [
|
|
515
|
+
{ city: 'New York' },
|
|
516
|
+
{ city: 'Los Angeles' },
|
|
517
|
+
{ city: 'Chicago' }
|
|
518
|
+
]
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// Complex nested conditions
|
|
523
|
+
await db.select({
|
|
524
|
+
table: 'orders',
|
|
525
|
+
where: {
|
|
526
|
+
status: 'pending',
|
|
527
|
+
$or: [
|
|
528
|
+
{ total: { $gte: 1000 } },
|
|
529
|
+
{
|
|
530
|
+
$and: [
|
|
531
|
+
{ priority: 'high' },
|
|
532
|
+
{ customer_type: 'vip' }
|
|
533
|
+
]
|
|
534
|
+
}
|
|
535
|
+
]
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// BETWEEN operator
|
|
540
|
+
await db.select({
|
|
541
|
+
table: 'products',
|
|
542
|
+
where: {
|
|
543
|
+
price: { $between: [10, 100] },
|
|
544
|
+
created_at: { $between: ['2024-01-01', '2024-12-31'] }
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// LIKE patterns
|
|
549
|
+
await db.select({
|
|
550
|
+
table: 'users',
|
|
551
|
+
where: {
|
|
552
|
+
email: { $like: '%@gmail.com' },
|
|
553
|
+
name: { $like: 'John%' }
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// IN and NOT IN
|
|
558
|
+
await db.select({
|
|
559
|
+
table: 'products',
|
|
560
|
+
where: {
|
|
561
|
+
category_id: { $in: [1, 2, 3, 4, 5] },
|
|
562
|
+
status: { $nin: ['discontinued', 'out_of_stock'] }
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
### JOIN Operations
|
|
570
|
+
|
|
571
|
+
The query builder supports all standard SQL JOIN types.
|
|
572
|
+
|
|
573
|
+
#### JOIN Syntax
|
|
574
|
+
|
|
575
|
+
```javascript
|
|
576
|
+
await db.select({
|
|
577
|
+
table: 'orders',
|
|
578
|
+
alias: 'o',
|
|
579
|
+
fields: ['o.id', 'o.total', 'u.name as user_name', 'p.name as product_name'],
|
|
580
|
+
joins: [
|
|
581
|
+
{
|
|
582
|
+
type: 'INNER', // JOIN type: INNER, LEFT, RIGHT, FULL
|
|
583
|
+
table: 'users', // Table to join
|
|
584
|
+
alias: 'u', // Table alias (optional)
|
|
585
|
+
on: 'o.user_id = u.id' // JOIN condition
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
type: 'LEFT',
|
|
589
|
+
table: 'products',
|
|
590
|
+
alias: 'p',
|
|
591
|
+
on: 'o.product_id = p.id'
|
|
592
|
+
}
|
|
593
|
+
],
|
|
594
|
+
where: { 'o.status': 'completed' },
|
|
595
|
+
orderBy: 'o.created_at DESC'
|
|
596
|
+
});
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
#### Multiple ON Conditions
|
|
600
|
+
|
|
601
|
+
```javascript
|
|
602
|
+
await db.select({
|
|
603
|
+
table: 'order_items',
|
|
604
|
+
alias: 'oi',
|
|
605
|
+
joins: [
|
|
606
|
+
{
|
|
607
|
+
type: 'INNER',
|
|
608
|
+
table: 'products',
|
|
609
|
+
alias: 'p',
|
|
610
|
+
on: ['oi.product_id = p.id', 'p.active = 1'] // Array for multiple conditions
|
|
611
|
+
}
|
|
612
|
+
]
|
|
613
|
+
});
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
### Transactions
|
|
619
|
+
|
|
620
|
+
Transactions ensure that multiple database operations succeed or fail together (ACID compliance).
|
|
621
|
+
|
|
622
|
+
#### Creating a Transaction
|
|
623
|
+
|
|
624
|
+
```javascript
|
|
625
|
+
// Method 1: Using createTransaction()
|
|
626
|
+
const transaction = db.createTransaction();
|
|
627
|
+
|
|
628
|
+
// Method 2: Using TransactionCRUD class directly
|
|
629
|
+
const transaction = new db.TransactionCRUD();
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
#### Basic Transaction Flow
|
|
633
|
+
|
|
634
|
+
```javascript
|
|
635
|
+
const transaction = db.createTransaction();
|
|
636
|
+
|
|
637
|
+
try {
|
|
638
|
+
// Initialize transaction
|
|
639
|
+
await transaction.init();
|
|
640
|
+
|
|
641
|
+
// Perform multiple operations
|
|
642
|
+
const userId = await transaction.insert('users', {
|
|
643
|
+
name: 'John',
|
|
644
|
+
email: 'john@example.com'
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
await transaction.insert('user_profiles', {
|
|
648
|
+
user_id: userId,
|
|
649
|
+
bio: 'Software Developer'
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
await transaction.insert('user_settings', {
|
|
653
|
+
user_id: userId,
|
|
654
|
+
theme: 'dark',
|
|
655
|
+
notifications: true
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
// Commit all changes
|
|
659
|
+
await transaction.commit();
|
|
660
|
+
console.log('Transaction successful');
|
|
661
|
+
|
|
662
|
+
} catch (error) {
|
|
663
|
+
// Rollback on any error
|
|
664
|
+
await transaction.rollback();
|
|
665
|
+
console.error('Transaction failed:', error);
|
|
666
|
+
throw error;
|
|
667
|
+
}
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
#### TransactionCRUD Methods
|
|
671
|
+
|
|
672
|
+
The `TransactionCRUD` class provides all CRUD methods available in the main API:
|
|
673
|
+
|
|
674
|
+
**Basic Methods:**
|
|
675
|
+
| Method | Description |
|
|
676
|
+
|--------|-------------|
|
|
677
|
+
| `init(dbConfig?)` | Initialize transaction |
|
|
678
|
+
| `commit()` | Commit transaction |
|
|
679
|
+
| `rollback()` | Rollback transaction |
|
|
680
|
+
| `executeQuery(query, params?)` | Execute raw query |
|
|
681
|
+
|
|
682
|
+
**CRUD Methods:**
|
|
683
|
+
| Method | Description |
|
|
684
|
+
|--------|-------------|
|
|
685
|
+
| `find(query, params?)` | Execute SELECT query |
|
|
686
|
+
| `insert(table, data)` | Insert record |
|
|
687
|
+
| `update(table, data, whereClause)` | Update records |
|
|
688
|
+
| `delete(whereClause, table)` | Delete records |
|
|
689
|
+
|
|
690
|
+
**Query Builder Methods (Verbose):**
|
|
691
|
+
| Method | Description |
|
|
692
|
+
|--------|-------------|
|
|
693
|
+
| `buildAndExecuteSelectQuery(options)` | Build & execute SELECT |
|
|
694
|
+
| `buildAndExecuteUpdateQuery(options)` | Build & execute UPDATE |
|
|
695
|
+
| `buildAndExecuteDeleteQuery(options)` | Build & execute DELETE |
|
|
696
|
+
|
|
697
|
+
**Query Builder Methods (Short Aliases):**
|
|
698
|
+
| Alias | Maps To |
|
|
699
|
+
|-------|---------|
|
|
700
|
+
| `select(options)` | `buildAndExecuteSelectQuery` |
|
|
701
|
+
| `findWhere(options)` | `buildAndExecuteSelectQuery` |
|
|
702
|
+
| `query(options)` | `buildAndExecuteSelectQuery` |
|
|
703
|
+
| `updateWhere(options)` | `buildAndExecuteUpdateQuery` |
|
|
704
|
+
| `updateQuery(options)` | `buildAndExecuteUpdateQuery` |
|
|
705
|
+
| `deleteWhere(options)` | `buildAndExecuteDeleteQuery` |
|
|
706
|
+
| `remove(options)` | `buildAndExecuteDeleteQuery` |
|
|
707
|
+
|
|
708
|
+
#### Complete Transaction Example
|
|
709
|
+
|
|
710
|
+
```javascript
|
|
711
|
+
const transaction = db.createTransaction();
|
|
712
|
+
|
|
713
|
+
try {
|
|
714
|
+
await transaction.init();
|
|
715
|
+
|
|
716
|
+
// Insert using basic method
|
|
717
|
+
const orderId = await transaction.insert('orders', {
|
|
718
|
+
user_id: 123,
|
|
719
|
+
total: 299.99,
|
|
720
|
+
status: 'pending'
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
// Insert using transaction
|
|
724
|
+
await transaction.insert('order_items', {
|
|
725
|
+
order_id: orderId,
|
|
726
|
+
product_id: 456,
|
|
727
|
+
quantity: 2,
|
|
728
|
+
price: 149.99
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
// Update using query builder (short alias)
|
|
732
|
+
await transaction.updateWhere({
|
|
733
|
+
table: 'products',
|
|
734
|
+
data: { stock: { __raw: true, value: 'stock - 2' } },
|
|
735
|
+
where: { id: 456 }
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
// Select within transaction
|
|
739
|
+
const inventory = await transaction.select({
|
|
740
|
+
table: 'products',
|
|
741
|
+
fields: ['id', 'stock'],
|
|
742
|
+
where: { id: 456 }
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
// Check stock
|
|
746
|
+
if (inventory[0].stock < 0) {
|
|
747
|
+
throw new Error('Insufficient stock');
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Find using raw query
|
|
751
|
+
const user = await transaction.find(
|
|
752
|
+
'SELECT email FROM users WHERE id = ?',
|
|
753
|
+
[123]
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
// Commit all changes
|
|
757
|
+
await transaction.commit();
|
|
758
|
+
|
|
759
|
+
return { orderId, userEmail: user[0].email };
|
|
760
|
+
|
|
761
|
+
} catch (error) {
|
|
762
|
+
await transaction.rollback();
|
|
763
|
+
throw error;
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
769
|
+
### Transaction Manager
|
|
770
|
+
|
|
771
|
+
The Transaction Manager allows you to share a transaction instance across different modules or files in your application.
|
|
772
|
+
|
|
773
|
+
#### API
|
|
774
|
+
|
|
775
|
+
| Method | Description |
|
|
776
|
+
|--------|-------------|
|
|
777
|
+
| `setInstance(transaction)` | Store transaction instance |
|
|
778
|
+
| `getInstance()` | Retrieve stored instance |
|
|
779
|
+
| `clearInstance()` | Clear stored instance |
|
|
780
|
+
| `hasActiveTransaction()` | Check if active transaction exists |
|
|
781
|
+
|
|
782
|
+
#### Usage Pattern
|
|
783
|
+
|
|
784
|
+
```javascript
|
|
785
|
+
// ========== Main file (e.g., orderController.js) ==========
|
|
786
|
+
const db = require('mysql-orm-lite');
|
|
787
|
+
const { processPayment } = require('./paymentService');
|
|
788
|
+
const { updateInventory } = require('./inventoryService');
|
|
789
|
+
|
|
790
|
+
async function createOrder(orderData) {
|
|
791
|
+
const transaction = db.createTransaction();
|
|
792
|
+
|
|
793
|
+
try {
|
|
794
|
+
await transaction.init();
|
|
795
|
+
|
|
796
|
+
// Store transaction for other modules
|
|
797
|
+
db.transactionManager.setInstance(transaction);
|
|
798
|
+
|
|
799
|
+
// Insert order
|
|
800
|
+
const orderId = await transaction.insert('orders', orderData);
|
|
801
|
+
|
|
802
|
+
// These functions will use the shared transaction
|
|
803
|
+
await processPayment(orderId, orderData.total);
|
|
804
|
+
await updateInventory(orderData.items);
|
|
805
|
+
|
|
806
|
+
await transaction.commit();
|
|
807
|
+
return orderId;
|
|
808
|
+
|
|
809
|
+
} catch (error) {
|
|
810
|
+
await transaction.rollback();
|
|
811
|
+
throw error;
|
|
812
|
+
} finally {
|
|
813
|
+
// Always clear the instance
|
|
814
|
+
db.transactionManager.clearInstance();
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// ========== paymentService.js ==========
|
|
819
|
+
const db = require('mysql-orm-lite');
|
|
820
|
+
|
|
821
|
+
async function processPayment(orderId, amount) {
|
|
822
|
+
const transaction = db.transactionManager.getInstance();
|
|
823
|
+
|
|
824
|
+
if (!transaction) {
|
|
825
|
+
throw new Error('No active transaction');
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
await transaction.insert('payments', {
|
|
829
|
+
order_id: orderId,
|
|
830
|
+
amount: amount,
|
|
831
|
+
status: 'completed'
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
module.exports = { processPayment };
|
|
836
|
+
|
|
837
|
+
// ========== inventoryService.js ==========
|
|
838
|
+
const db = require('mysql-orm-lite');
|
|
839
|
+
|
|
840
|
+
async function updateInventory(items) {
|
|
841
|
+
const transaction = db.transactionManager.getInstance();
|
|
842
|
+
|
|
843
|
+
if (!transaction) {
|
|
844
|
+
throw new Error('No active transaction');
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
for (const item of items) {
|
|
848
|
+
await transaction.updateWhere({
|
|
849
|
+
table: 'products',
|
|
850
|
+
data: { stock: { __raw: true, value: `stock - ${item.quantity}` } },
|
|
851
|
+
where: { id: item.product_id }
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
module.exports = { updateInventory };
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
#### Checking Transaction State
|
|
860
|
+
|
|
861
|
+
```javascript
|
|
862
|
+
if (db.transactionManager.hasActiveTransaction()) {
|
|
863
|
+
const transaction = db.transactionManager.getInstance();
|
|
864
|
+
await transaction.insert('logs', { message: 'Within transaction' });
|
|
865
|
+
} else {
|
|
866
|
+
await db.insert('logs', { message: 'No transaction' });
|
|
867
|
+
}
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
---
|
|
871
|
+
|
|
872
|
+
## Advanced Usage
|
|
873
|
+
|
|
874
|
+
### Multiple Database Connections
|
|
875
|
+
|
|
876
|
+
Connect to multiple MySQL databases simultaneously:
|
|
877
|
+
|
|
878
|
+
```javascript
|
|
879
|
+
// Define database configurations
|
|
880
|
+
const primaryDB = {
|
|
881
|
+
host: 'primary-server.com',
|
|
882
|
+
user: 'admin',
|
|
883
|
+
password: 'secret',
|
|
884
|
+
database: 'main_app',
|
|
885
|
+
connectionLimit: 20
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
const analyticsDB = {
|
|
889
|
+
host: 'analytics-server.com',
|
|
890
|
+
user: 'reader',
|
|
891
|
+
password: 'readonly',
|
|
892
|
+
database: 'analytics',
|
|
893
|
+
connectionLimit: 5
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
const replicaDB = {
|
|
897
|
+
host: 'replica-server.com',
|
|
898
|
+
user: 'reader',
|
|
899
|
+
password: 'secret',
|
|
900
|
+
database: 'main_app',
|
|
901
|
+
connectionLimit: 10
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
// Initialize with default database
|
|
905
|
+
db.connectionManager.init(primaryDB);
|
|
906
|
+
|
|
907
|
+
// Use default connection
|
|
908
|
+
const users = await db.find('SELECT * FROM users');
|
|
909
|
+
|
|
910
|
+
// Use analytics database
|
|
911
|
+
const events = await db.find(
|
|
912
|
+
'SELECT * FROM events WHERE date > ?',
|
|
913
|
+
['2024-01-01'],
|
|
914
|
+
analyticsDB
|
|
915
|
+
);
|
|
916
|
+
|
|
917
|
+
// Use replica for read-heavy operations
|
|
918
|
+
const reports = await db.select({
|
|
919
|
+
table: 'orders',
|
|
920
|
+
where: { status: 'completed' },
|
|
921
|
+
orderBy: 'created_at DESC',
|
|
922
|
+
limit: 1000
|
|
923
|
+
}, replicaDB);
|
|
924
|
+
|
|
925
|
+
// Write to primary, read from replica
|
|
926
|
+
await db.insert('orders', orderData, primaryDB);
|
|
927
|
+
const order = await db.find('SELECT * FROM orders WHERE id = ?', [orderId], replicaDB);
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
### Raw SQL Expressions
|
|
931
|
+
|
|
932
|
+
Use raw SQL expressions in updates:
|
|
933
|
+
|
|
934
|
+
```javascript
|
|
935
|
+
// Increment a value
|
|
936
|
+
await db.updateWhere({
|
|
937
|
+
table: 'products',
|
|
938
|
+
data: {
|
|
939
|
+
stock: { __raw: true, value: 'stock - 1' },
|
|
940
|
+
view_count: { __raw: true, value: 'view_count + 1' },
|
|
941
|
+
updated_at: new Date()
|
|
942
|
+
},
|
|
943
|
+
where: { id: productId }
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
// Use MySQL functions
|
|
947
|
+
await db.updateWhere({
|
|
948
|
+
table: 'users',
|
|
949
|
+
data: {
|
|
950
|
+
last_login: { __raw: true, value: 'NOW()' },
|
|
951
|
+
login_count: { __raw: true, value: 'login_count + 1' }
|
|
952
|
+
},
|
|
953
|
+
where: { id: userId }
|
|
954
|
+
});
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### Custom Logger Integration
|
|
958
|
+
|
|
959
|
+
#### Winston Logger
|
|
960
|
+
|
|
961
|
+
```javascript
|
|
962
|
+
const winston = require('winston');
|
|
963
|
+
|
|
964
|
+
const logger = winston.createLogger({
|
|
965
|
+
level: 'info',
|
|
966
|
+
format: winston.format.combine(
|
|
967
|
+
winston.format.timestamp(),
|
|
968
|
+
winston.format.json()
|
|
969
|
+
),
|
|
970
|
+
transports: [
|
|
971
|
+
new winston.transports.File({ filename: 'error.log', level: 'error' }),
|
|
972
|
+
new winston.transports.File({ filename: 'database.log' }),
|
|
973
|
+
new winston.transports.Console({
|
|
974
|
+
format: winston.format.simple()
|
|
975
|
+
})
|
|
976
|
+
]
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
db.connectionManager.init(dbConfig, logger);
|
|
980
|
+
// or
|
|
981
|
+
db.connectionManager.setLogger(logger);
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
#### Bunyan Logger
|
|
985
|
+
|
|
986
|
+
```javascript
|
|
987
|
+
const bunyan = require('bunyan');
|
|
988
|
+
|
|
989
|
+
const logger = bunyan.createLogger({
|
|
990
|
+
name: 'mysql-orm',
|
|
991
|
+
level: 'info'
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
db.connectionManager.setLogger(logger);
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
#### Pino Logger
|
|
998
|
+
|
|
999
|
+
```javascript
|
|
1000
|
+
const pino = require('pino');
|
|
1001
|
+
|
|
1002
|
+
const logger = pino({
|
|
1003
|
+
level: 'info',
|
|
1004
|
+
transport: {
|
|
1005
|
+
target: 'pino-pretty'
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
db.connectionManager.setLogger(logger);
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
---
|
|
1013
|
+
|
|
1014
|
+
## Error Handling
|
|
1015
|
+
|
|
1016
|
+
### Common Error Codes
|
|
1017
|
+
|
|
1018
|
+
| Error Code | Description |
|
|
1019
|
+
|------------|-------------|
|
|
1020
|
+
| `ER_DUP_ENTRY` | Duplicate key violation |
|
|
1021
|
+
| `ER_NO_REFERENCED_ROW` | Foreign key constraint fails |
|
|
1022
|
+
| `ER_ROW_IS_REFERENCED` | Cannot delete parent row |
|
|
1023
|
+
| `ER_BAD_FIELD_ERROR` | Unknown column |
|
|
1024
|
+
| `ER_NO_SUCH_TABLE` | Table doesn't exist |
|
|
1025
|
+
| `PROTOCOL_CONNECTION_LOST` | Connection lost |
|
|
1026
|
+
| `ECONNREFUSED` | Connection refused |
|
|
1027
|
+
| `ER_CON_COUNT_ERROR` | Too many connections |
|
|
1028
|
+
|
|
1029
|
+
### Error Handling Examples
|
|
1030
|
+
|
|
1031
|
+
```javascript
|
|
1032
|
+
try {
|
|
1033
|
+
await db.insert('users', { email: 'existing@example.com' });
|
|
1034
|
+
} catch (error) {
|
|
1035
|
+
switch (error.code) {
|
|
1036
|
+
case 'ER_DUP_ENTRY':
|
|
1037
|
+
console.error('Email already exists');
|
|
1038
|
+
break;
|
|
1039
|
+
case 'ER_NO_SUCH_TABLE':
|
|
1040
|
+
console.error('Table does not exist');
|
|
1041
|
+
break;
|
|
1042
|
+
case 'ECONNREFUSED':
|
|
1043
|
+
console.error('Cannot connect to database');
|
|
1044
|
+
break;
|
|
1045
|
+
default:
|
|
1046
|
+
console.error('Database error:', error.message);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
### Transaction Error Handling
|
|
1052
|
+
|
|
1053
|
+
```javascript
|
|
1054
|
+
const transaction = db.createTransaction();
|
|
1055
|
+
|
|
1056
|
+
try {
|
|
1057
|
+
await transaction.init();
|
|
1058
|
+
|
|
1059
|
+
await transaction.insert('orders', orderData);
|
|
1060
|
+
await transaction.insert('order_items', itemsData);
|
|
1061
|
+
|
|
1062
|
+
await transaction.commit();
|
|
1063
|
+
} catch (error) {
|
|
1064
|
+
// Rollback is safe to call multiple times
|
|
1065
|
+
await transaction.rollback();
|
|
1066
|
+
|
|
1067
|
+
if (error.code === 'ER_DUP_ENTRY') {
|
|
1068
|
+
throw new Error('Order already exists');
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
throw error;
|
|
1072
|
+
}
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
---
|
|
1076
|
+
|
|
1077
|
+
## Performance Tips
|
|
1078
|
+
|
|
1079
|
+
1. **Use Connection Pooling** (Built-in)
|
|
1080
|
+
- Connections are reused automatically
|
|
1081
|
+
- Set `connectionLimit` based on your needs
|
|
1082
|
+
|
|
1083
|
+
2. **Monitor Slow Queries**
|
|
1084
|
+
- Queries taking >1 second are automatically logged
|
|
1085
|
+
- Review and optimize slow queries regularly
|
|
1086
|
+
|
|
1087
|
+
3. **Use Transactions Wisely**
|
|
1088
|
+
- Group related operations in transactions
|
|
1089
|
+
- Keep transactions short to avoid lock contention
|
|
1090
|
+
|
|
1091
|
+
4. **Use Parameterized Queries**
|
|
1092
|
+
- Always use `?` placeholders
|
|
1093
|
+
- Never concatenate user input into queries
|
|
1094
|
+
|
|
1095
|
+
5. **Select Only Needed Fields**
|
|
1096
|
+
```javascript
|
|
1097
|
+
// Good - select specific fields
|
|
1098
|
+
await db.select({
|
|
1099
|
+
table: 'users',
|
|
1100
|
+
fields: ['id', 'name', 'email']
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
// Avoid - selecting all fields
|
|
1104
|
+
await db.select({ table: 'users' }); // SELECT *
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
6. **Use Indexes**
|
|
1108
|
+
- Add indexes on frequently queried columns
|
|
1109
|
+
- Use EXPLAIN to analyze query performance
|
|
1110
|
+
|
|
1111
|
+
7. **Pagination**
|
|
1112
|
+
```javascript
|
|
1113
|
+
// Always use limit and offset for large datasets
|
|
1114
|
+
await db.select({
|
|
1115
|
+
table: 'logs',
|
|
1116
|
+
orderBy: 'created_at DESC',
|
|
1117
|
+
limit: 50,
|
|
1118
|
+
offset: page * 50
|
|
1119
|
+
});
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
8. **Close Pools on Shutdown**
|
|
1123
|
+
```javascript
|
|
1124
|
+
process.on('SIGINT', async () => {
|
|
1125
|
+
await db.connectionManager.closeAllPools();
|
|
1126
|
+
process.exit(0);
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
process.on('SIGTERM', async () => {
|
|
1130
|
+
await db.connectionManager.closeAllPools();
|
|
1131
|
+
process.exit(0);
|
|
1132
|
+
});
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
---
|
|
1136
|
+
|
|
1137
|
+
## Complete Examples
|
|
1138
|
+
|
|
1139
|
+
### User Service Class
|
|
1140
|
+
|
|
1141
|
+
```javascript
|
|
1142
|
+
const db = require('mysql-orm-lite');
|
|
1143
|
+
|
|
1144
|
+
class UserService {
|
|
1145
|
+
constructor() {
|
|
1146
|
+
db.connectionManager.init({
|
|
1147
|
+
host: process.env.DB_HOST || 'localhost',
|
|
1148
|
+
user: process.env.DB_USER || 'root',
|
|
1149
|
+
password: process.env.DB_PASSWORD,
|
|
1150
|
+
database: process.env.DB_NAME,
|
|
1151
|
+
connectionLimit: 10
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
async createUser(userData) {
|
|
1156
|
+
const transaction = db.createTransaction();
|
|
1157
|
+
|
|
1158
|
+
try {
|
|
1159
|
+
await transaction.init();
|
|
1160
|
+
|
|
1161
|
+
const userId = await transaction.insert('users', {
|
|
1162
|
+
email: userData.email,
|
|
1163
|
+
password: userData.hashedPassword,
|
|
1164
|
+
created_at: new Date()
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
await transaction.insert('profiles', {
|
|
1168
|
+
user_id: userId,
|
|
1169
|
+
first_name: userData.firstName,
|
|
1170
|
+
last_name: userData.lastName,
|
|
1171
|
+
avatar: userData.avatar || null
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
await transaction.insert('user_settings', {
|
|
1175
|
+
user_id: userId,
|
|
1176
|
+
theme: 'system',
|
|
1177
|
+
notifications: true,
|
|
1178
|
+
language: 'en'
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
await transaction.commit();
|
|
1182
|
+
return userId;
|
|
1183
|
+
} catch (error) {
|
|
1184
|
+
await transaction.rollback();
|
|
1185
|
+
throw error;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
async getUser(userId) {
|
|
1190
|
+
const users = await db.select({
|
|
1191
|
+
table: 'users',
|
|
1192
|
+
alias: 'u',
|
|
1193
|
+
fields: ['u.id', 'u.email', 'p.first_name', 'p.last_name', 'p.avatar'],
|
|
1194
|
+
joins: [{
|
|
1195
|
+
type: 'LEFT',
|
|
1196
|
+
table: 'profiles',
|
|
1197
|
+
alias: 'p',
|
|
1198
|
+
on: 'u.id = p.user_id'
|
|
1199
|
+
}],
|
|
1200
|
+
where: { 'u.id': userId }
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
return users[0] || null;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
async getUsers(filters = {}, page = 1, limit = 20) {
|
|
1207
|
+
const offset = (page - 1) * limit;
|
|
1208
|
+
|
|
1209
|
+
const where = { deleted_at: null };
|
|
1210
|
+
if (filters.status) where.status = filters.status;
|
|
1211
|
+
if (filters.search) where.email = { $like: `%${filters.search}%` };
|
|
1212
|
+
|
|
1213
|
+
const users = await db.select({
|
|
1214
|
+
table: 'users',
|
|
1215
|
+
fields: ['id', 'email', 'status', 'created_at'],
|
|
1216
|
+
where,
|
|
1217
|
+
orderBy: 'created_at DESC',
|
|
1218
|
+
limit,
|
|
1219
|
+
offset
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
const total = await db.findCount(
|
|
1223
|
+
'SELECT COUNT(*) as count FROM users WHERE deleted_at IS NULL'
|
|
1224
|
+
);
|
|
1225
|
+
|
|
1226
|
+
return {
|
|
1227
|
+
data: users,
|
|
1228
|
+
pagination: {
|
|
1229
|
+
page,
|
|
1230
|
+
limit,
|
|
1231
|
+
total,
|
|
1232
|
+
pages: Math.ceil(total / limit)
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
async updateUser(userId, updates) {
|
|
1238
|
+
return await db.updateWhere({
|
|
1239
|
+
table: 'users',
|
|
1240
|
+
data: {
|
|
1241
|
+
...updates,
|
|
1242
|
+
updated_at: new Date()
|
|
1243
|
+
},
|
|
1244
|
+
where: { id: userId }
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
async deleteUser(userId) {
|
|
1249
|
+
// Soft delete
|
|
1250
|
+
return await db.updateWhere({
|
|
1251
|
+
table: 'users',
|
|
1252
|
+
data: {
|
|
1253
|
+
deleted_at: new Date(),
|
|
1254
|
+
status: 'deleted'
|
|
1255
|
+
},
|
|
1256
|
+
where: { id: userId }
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
module.exports = new UserService();
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
### Express.js Integration
|
|
1265
|
+
|
|
1266
|
+
```javascript
|
|
1267
|
+
const express = require('express');
|
|
1268
|
+
const db = require('mysql-orm-lite');
|
|
1269
|
+
|
|
1270
|
+
const app = express();
|
|
1271
|
+
app.use(express.json());
|
|
1272
|
+
|
|
1273
|
+
// Initialize database
|
|
1274
|
+
db.connectionManager.init({
|
|
1275
|
+
host: process.env.DB_HOST,
|
|
1276
|
+
user: process.env.DB_USER,
|
|
1277
|
+
password: process.env.DB_PASSWORD,
|
|
1278
|
+
database: process.env.DB_NAME
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
// Routes
|
|
1282
|
+
app.get('/users', async (req, res) => {
|
|
1283
|
+
try {
|
|
1284
|
+
const { page = 1, limit = 20, status } = req.query;
|
|
1285
|
+
|
|
1286
|
+
const where = {};
|
|
1287
|
+
if (status) where.status = status;
|
|
1288
|
+
|
|
1289
|
+
const users = await db.select({
|
|
1290
|
+
table: 'users',
|
|
1291
|
+
fields: ['id', 'name', 'email', 'status'],
|
|
1292
|
+
where,
|
|
1293
|
+
orderBy: 'created_at DESC',
|
|
1294
|
+
limit: parseInt(limit),
|
|
1295
|
+
offset: (parseInt(page) - 1) * parseInt(limit)
|
|
1296
|
+
});
|
|
1297
|
+
|
|
1298
|
+
res.json({ success: true, data: users });
|
|
1299
|
+
} catch (error) {
|
|
1300
|
+
res.status(500).json({ success: false, error: error.message });
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
|
|
1304
|
+
app.post('/users', async (req, res) => {
|
|
1305
|
+
try {
|
|
1306
|
+
const id = await db.insert('users', req.body);
|
|
1307
|
+
res.status(201).json({ success: true, id });
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
if (error.code === 'ER_DUP_ENTRY') {
|
|
1310
|
+
res.status(409).json({ success: false, error: 'User already exists' });
|
|
1311
|
+
} else {
|
|
1312
|
+
res.status(500).json({ success: false, error: error.message });
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
// Graceful shutdown
|
|
1318
|
+
const server = app.listen(3000);
|
|
1319
|
+
|
|
1320
|
+
process.on('SIGTERM', async () => {
|
|
1321
|
+
server.close();
|
|
1322
|
+
await db.connectionManager.closeAllPools();
|
|
1323
|
+
process.exit(0);
|
|
1324
|
+
});
|
|
1325
|
+
```
|
|
1326
|
+
|
|
1327
|
+
---
|
|
1328
|
+
|
|
1329
|
+
## API Quick Reference
|
|
1330
|
+
|
|
1331
|
+
### Main Exports
|
|
1332
|
+
|
|
1333
|
+
```javascript
|
|
1334
|
+
const db = require('mysql-orm-lite');
|
|
1335
|
+
|
|
1336
|
+
// Connection
|
|
1337
|
+
db.connectionManager.init(config, logger?)
|
|
1338
|
+
db.connectionManager.setLogger(logger)
|
|
1339
|
+
db.connectionManager.getPool(config?)
|
|
1340
|
+
db.connectionManager.closePool(config)
|
|
1341
|
+
db.connectionManager.closeAllPools()
|
|
1342
|
+
db.connectionManager.getLogger()
|
|
1343
|
+
|
|
1344
|
+
// CRUD
|
|
1345
|
+
db.insert(table, data, dbConfig?, debug?, isIgnore?)
|
|
1346
|
+
db.update(table, data, whereClause, dbConfig?, debug?)
|
|
1347
|
+
db.delete(whereClause, table, dbConfig?)
|
|
1348
|
+
db.find(query, params?, dbConfig?)
|
|
1349
|
+
db.findCount(query, params?, dbConfig?)
|
|
1350
|
+
|
|
1351
|
+
// Query Builder
|
|
1352
|
+
db.select(options, dbConfig?) // alias: findWhere, query
|
|
1353
|
+
db.buildAndExecuteSelectQuery(options, dbConfig?)
|
|
1354
|
+
db.updateWhere(options, dbConfig?) // alias: updateQuery
|
|
1355
|
+
db.buildAndExecuteUpdateQuery(options, dbConfig?)
|
|
1356
|
+
db.deleteWhere(options, dbConfig?) // alias: remove
|
|
1357
|
+
db.buildAndExecuteDeleteQuery(options, dbConfig?)
|
|
1358
|
+
|
|
1359
|
+
// Transactions
|
|
1360
|
+
db.createTransaction()
|
|
1361
|
+
db.TransactionCRUD
|
|
1362
|
+
db.transactionManager.setInstance(transaction)
|
|
1363
|
+
db.transactionManager.getInstance()
|
|
1364
|
+
db.transactionManager.clearInstance()
|
|
1365
|
+
db.transactionManager.hasActiveTransaction()
|
|
1366
|
+
|
|
1367
|
+
// Utilities
|
|
1368
|
+
db.utils
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1371
|
+
### TransactionCRUD Methods
|
|
1372
|
+
|
|
1373
|
+
```javascript
|
|
1374
|
+
const transaction = db.createTransaction();
|
|
1375
|
+
|
|
1376
|
+
// Lifecycle
|
|
1377
|
+
await transaction.init(dbConfig?)
|
|
1378
|
+
await transaction.commit()
|
|
1379
|
+
await transaction.rollback()
|
|
1380
|
+
|
|
1381
|
+
// Raw query
|
|
1382
|
+
await transaction.executeQuery(query, params?)
|
|
1383
|
+
await transaction.find(query, params?)
|
|
1384
|
+
|
|
1385
|
+
// CRUD
|
|
1386
|
+
await transaction.insert(table, data)
|
|
1387
|
+
await transaction.update(table, data, whereClause)
|
|
1388
|
+
await transaction.delete(whereClause, table)
|
|
1389
|
+
|
|
1390
|
+
// Query Builder (verbose)
|
|
1391
|
+
await transaction.buildAndExecuteSelectQuery(options)
|
|
1392
|
+
await transaction.buildAndExecuteUpdateQuery(options)
|
|
1393
|
+
await transaction.buildAndExecuteDeleteQuery(options)
|
|
1394
|
+
|
|
1395
|
+
// Query Builder (aliases)
|
|
1396
|
+
await transaction.select(options)
|
|
1397
|
+
await transaction.findWhere(options)
|
|
1398
|
+
await transaction.query(options)
|
|
1399
|
+
await transaction.updateWhere(options)
|
|
1400
|
+
await transaction.updateQuery(options)
|
|
1401
|
+
await transaction.deleteWhere(options)
|
|
1402
|
+
await transaction.remove(options)
|
|
1403
|
+
```
|
|
1404
|
+
|
|
1405
|
+
---
|
|
1406
|
+
|
|
1407
|
+
## License
|
|
1408
|
+
|
|
1409
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
1410
|
+
|
|
1411
|
+
## Contributing
|
|
1412
|
+
|
|
1413
|
+
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
|
1414
|
+
|
|
1415
|
+
## Support
|
|
1416
|
+
|
|
1417
|
+
For issues and questions: [GitHub Issues](https://github.com/Manikandan-Thonthiraj/mysql-orm-lite/issues)
|
|
1418
|
+
|
|
1419
|
+
---
|
|
1420
|
+
|
|
1421
|
+
Made with âĪïļ for the Node.js community
|