mm_mysql 2.2.4 → 2.2.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 +3 -4
- package/README_EN.md +478 -0
- package/db.js +497 -496
- package/eslint.config.js +235 -0
- package/index.js +601 -601
- package/package.json +12 -5
- package/sql.js +1390 -1276
- package/test.js +481 -485
package/index.js
CHANGED
|
@@ -1,602 +1,602 @@
|
|
|
1
|
-
const mysql = require('mysql2/promise');
|
|
2
|
-
const { Base } = require('mm_expand');
|
|
3
|
-
const { DB } = require('./db');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 优化版MySQL数据库操作类
|
|
7
|
-
* 保持必要功能,简化过度封装,直接使用mysql2模块
|
|
8
|
-
* @class Mysql
|
|
9
|
-
* @
|
|
10
|
-
*/
|
|
11
|
-
class Mysql extends Base {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* 获取MySQL2支持的配置参数
|
|
48
|
-
* 根据连接类型(连接池或单连接)过滤参数,避免警告
|
|
49
|
-
* @param {boolean} is_pool - 是否为连接池配置
|
|
50
|
-
* @returns {
|
|
51
|
-
*/
|
|
52
|
-
Mysql.prototype._getValidConfig = function (is_pool = false) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* 打开数据库连接
|
|
105
|
-
* @returns {Promise<boolean>}
|
|
106
|
-
* @throws {TypeError} 当配置参数无效时
|
|
107
|
-
*/
|
|
108
|
-
Mysql.prototype.open = async function () {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* 关闭数据库连接
|
|
137
|
-
* @returns {Promise<boolean>}
|
|
138
|
-
*/
|
|
139
|
-
Mysql.prototype.close = async function () {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 安全释放数据库连接
|
|
160
|
-
* @param {
|
|
161
|
-
* @param {boolean} is_pool_conn - 是否为连接池连接
|
|
162
|
-
* @returns {Promise<void>}
|
|
163
|
-
*/
|
|
164
|
-
async function safeReleaseConnection(conn, is_pool_conn) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* 获取数据库连接(改进版)
|
|
178
|
-
* @returns {Promise<
|
|
179
|
-
* @throws {TypeError} 当连接池或连接不存在时
|
|
180
|
-
*/
|
|
181
|
-
Mysql.prototype.getConn = async function () {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* 执行SQL查询(改进版)
|
|
202
|
-
* @param {
|
|
203
|
-
* @param {Array} params - 参数数组
|
|
204
|
-
* @returns {Promise<
|
|
205
|
-
* @throws {TypeError} 当sql参数无效时
|
|
206
|
-
*/
|
|
207
|
-
Mysql.prototype.run = async function (sql, params = []) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* 执行SQL语句(改进版)
|
|
257
|
-
* @param {
|
|
258
|
-
* @param {Array} params - 参数数组
|
|
259
|
-
* @returns {Promise<
|
|
260
|
-
* @throws {TypeError} 当sql参数无效时
|
|
261
|
-
*/
|
|
262
|
-
Mysql.prototype.exec = async function (sql, params = []) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* 开始事务(改进版)
|
|
312
|
-
* @returns {Promise<
|
|
313
|
-
*/
|
|
314
|
-
Mysql.prototype.beginTransaction = async function () {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* 在事务中执行多个操作(改进版)
|
|
367
|
-
* @param {Function} callback - 包含事务操作的回调函数
|
|
368
|
-
* @returns {Promise<*>} 回调函数的返回值
|
|
369
|
-
* @throws {TypeError} 当callback参数无效时
|
|
370
|
-
*/
|
|
371
|
-
Mysql.prototype.transaction = async function (callback) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* 读取整张表的数据(改进版)
|
|
408
|
-
* @param {
|
|
409
|
-
* @param {
|
|
410
|
-
* @param {
|
|
411
|
-
* @returns {Promise<Array>}
|
|
412
|
-
* @throws {TypeError} 当table参数无效时
|
|
413
|
-
*/
|
|
414
|
-
Mysql.prototype.read = async function (table, condition = {}, options = {}) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* 获取连接池状态信息
|
|
464
|
-
* @returns {
|
|
465
|
-
*/
|
|
466
|
-
Mysql.prototype.getPoolStats = function () {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* 健康检查
|
|
481
|
-
* @returns {Promise<
|
|
482
|
-
*/
|
|
483
|
-
Mysql.prototype.healthCheck = async function () {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* 获取数据库管理器(保持兼容性)
|
|
518
|
-
* @returns {
|
|
519
|
-
*/
|
|
520
|
-
Mysql.prototype.db = function () {
|
|
521
|
-
|
|
522
|
-
};
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* 初始化MySQL服务(保持兼容性)
|
|
526
|
-
* @returns {Promise<void>}
|
|
527
|
-
*/
|
|
528
|
-
Mysql.prototype.init = async function () {
|
|
529
|
-
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* 导出模块
|
|
534
|
-
*/
|
|
535
|
-
exports.Mysql = Mysql;
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* 确保连接池对象存在
|
|
539
|
-
*/
|
|
540
|
-
if (!$.pool) {
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
if (!$.pool.mysql) {
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* 全局锁对象,用于线程安全
|
|
549
|
-
*/
|
|
550
|
-
if (!$.pool.mysql._locks) {
|
|
551
|
-
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* 获取锁
|
|
556
|
-
* @param {string} key - 锁的键名
|
|
557
|
-
* @returns {Promise<Function>} 释放锁的函数
|
|
558
|
-
*/
|
|
559
|
-
function _acquireLock(key) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* @description Mysql管理器,用于创建缓存(线程安全版)
|
|
577
|
-
* @param {
|
|
578
|
-
* @param {
|
|
579
|
-
* @
|
|
580
|
-
*/
|
|
581
|
-
function mysqlAdmin(scope, config) {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* @module 导出Mysql管理器
|
|
601
|
-
*/
|
|
1
|
+
const mysql = require('mysql2/promise');
|
|
2
|
+
const { Base } = require('mm_expand');
|
|
3
|
+
const { DB } = require('./db');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 优化版MySQL数据库操作类
|
|
7
|
+
* 保持必要功能,简化过度封装,直接使用mysql2模块
|
|
8
|
+
* @class Mysql
|
|
9
|
+
* @augments Base
|
|
10
|
+
*/
|
|
11
|
+
class Mysql extends Base {
|
|
12
|
+
/**
|
|
13
|
+
* 默认配置
|
|
14
|
+
* MySQL2支持的连接池配置参数:
|
|
15
|
+
* - host, port, user, password, database
|
|
16
|
+
* - charset, timezone, connect_timeout
|
|
17
|
+
* - connection_limit, acquire_timeout, wait_for_connections
|
|
18
|
+
* - queueLimit参数在MySQL2中可能不被支持,已移除
|
|
19
|
+
*/
|
|
20
|
+
static default_config = {
|
|
21
|
+
host: '127.0.0.1',
|
|
22
|
+
port: 3306,
|
|
23
|
+
user: 'root',
|
|
24
|
+
password: 'Asd159357',
|
|
25
|
+
database: '',
|
|
26
|
+
charset: 'utf8mb4',
|
|
27
|
+
timezone: '+08:00',
|
|
28
|
+
connect_timeout: 20000,
|
|
29
|
+
connection_limit: 10,
|
|
30
|
+
acquire_timeout: 20000,
|
|
31
|
+
wait_for_connections: true
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 构造函数
|
|
36
|
+
* @param {object} config - 配置对象
|
|
37
|
+
*/
|
|
38
|
+
constructor(config = {}) {
|
|
39
|
+
super({ ...Mysql.default_config, ...config});
|
|
40
|
+
this._pool = null;
|
|
41
|
+
this._connection = null;
|
|
42
|
+
this._usePool = this.config.connection_limit > 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 获取MySQL2支持的配置参数
|
|
48
|
+
* 根据连接类型(连接池或单连接)过滤参数,避免警告
|
|
49
|
+
* @param {boolean} is_pool - 是否为连接池配置
|
|
50
|
+
* @returns {object} 过滤后的配置对象
|
|
51
|
+
*/
|
|
52
|
+
Mysql.prototype._getValidConfig = function (is_pool = false) {
|
|
53
|
+
// 基础连接参数(适用于所有连接类型)
|
|
54
|
+
const base_map = {
|
|
55
|
+
'host': 'host',
|
|
56
|
+
'port': 'port',
|
|
57
|
+
'user': 'user',
|
|
58
|
+
'password': 'password',
|
|
59
|
+
'database': 'database',
|
|
60
|
+
'charset': 'charset',
|
|
61
|
+
'timezone': 'timezone',
|
|
62
|
+
'connect_timeout': 'connectTimeout'
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// 连接池专用参数
|
|
66
|
+
const pool_map = {
|
|
67
|
+
'connection_limit': 'connectionLimit',
|
|
68
|
+
'wait_for_connections': 'waitForConnections',
|
|
69
|
+
'idle_timeout': 'idleTimeout',
|
|
70
|
+
'max_idle': 'maxIdle'
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const config = {};
|
|
74
|
+
|
|
75
|
+
// 添加基础参数
|
|
76
|
+
for (const key in base_map) {
|
|
77
|
+
if (this.config[key] !== undefined) {
|
|
78
|
+
config[base_map[key]] = this.config[key];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 如果是连接池,添加连接池专用参数
|
|
83
|
+
if (is_pool) {
|
|
84
|
+
for (const key in pool_map) {
|
|
85
|
+
if (this.config[key] !== undefined) {
|
|
86
|
+
config[pool_map[key]] = this.config[key];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// 如果是单连接,确保不包含任何连接池专用参数
|
|
91
|
+
else {
|
|
92
|
+
// 移除所有连接池专用参数,避免mysql2内部警告
|
|
93
|
+
for (const key in pool_map) {
|
|
94
|
+
if (pool_map[key] in config) {
|
|
95
|
+
delete config[pool_map[key]];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return config;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 打开数据库连接
|
|
105
|
+
* @returns {Promise<boolean>}
|
|
106
|
+
* @throws {TypeError} 当配置参数无效时
|
|
107
|
+
*/
|
|
108
|
+
Mysql.prototype.open = async function () {
|
|
109
|
+
// 参数校验
|
|
110
|
+
if (!this.config || typeof this.config !== 'object') {
|
|
111
|
+
throw new TypeError('config must be object');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
if (this._usePool) {
|
|
116
|
+
// 根据连接类型获取对应的配置参数
|
|
117
|
+
const valid_config = this._getValidConfig(this._usePool);
|
|
118
|
+
this._pool = mysql.createPool(valid_config);
|
|
119
|
+
// 测试连接池连接
|
|
120
|
+
const conn = await this._pool.getConnection();
|
|
121
|
+
conn.release();
|
|
122
|
+
} else {
|
|
123
|
+
const valid_config = this._getValidConfig(this._usePool);
|
|
124
|
+
this._connection = await mysql.createConnection(valid_config);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.log('info', '数据库连接成功');
|
|
128
|
+
return true;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this.log('error', '数据库连接失败', error);
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 关闭数据库连接
|
|
137
|
+
* @returns {Promise<boolean>}
|
|
138
|
+
*/
|
|
139
|
+
Mysql.prototype.close = async function () {
|
|
140
|
+
try {
|
|
141
|
+
if (this._pool) {
|
|
142
|
+
await this._pool.end();
|
|
143
|
+
this._pool = null;
|
|
144
|
+
}
|
|
145
|
+
if (this._connection) {
|
|
146
|
+
await this._connection.end();
|
|
147
|
+
this._connection = null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this.log('info', '数据库连接已关闭');
|
|
151
|
+
return true;
|
|
152
|
+
} catch (error) {
|
|
153
|
+
this.log('error', '关闭连接失败', error);
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 安全释放数据库连接
|
|
160
|
+
* @param {object} conn - 数据库连接对象
|
|
161
|
+
* @param {boolean} is_pool_conn - 是否为连接池连接
|
|
162
|
+
* @returns {Promise<void>}
|
|
163
|
+
*/
|
|
164
|
+
async function safeReleaseConnection(conn, is_pool_conn) {
|
|
165
|
+
if (!conn) return;
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
if (is_pool_conn && typeof conn.release === 'function') {
|
|
169
|
+
await conn.release();
|
|
170
|
+
}
|
|
171
|
+
} catch (release_err) {
|
|
172
|
+
$.log.error('释放连接失败', release_err);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 获取数据库连接(改进版)
|
|
178
|
+
* @returns {Promise<object>}
|
|
179
|
+
* @throws {TypeError} 当连接池或连接不存在时
|
|
180
|
+
*/
|
|
181
|
+
Mysql.prototype.getConn = async function () {
|
|
182
|
+
// 参数校验
|
|
183
|
+
if (!this._pool && !this._connection) {
|
|
184
|
+
await this.open();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
if (this._usePool) {
|
|
189
|
+
const conn = await this._pool.getConnection();
|
|
190
|
+
return conn;
|
|
191
|
+
} else {
|
|
192
|
+
return this._connection;
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
this.log('error', '获取连接失败', error);
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 执行SQL查询(改进版)
|
|
202
|
+
* @param {string} sql - SQL语句
|
|
203
|
+
* @param {Array} params - 参数数组
|
|
204
|
+
* @returns {Promise<object>}
|
|
205
|
+
* @throws {TypeError} 当sql参数无效时
|
|
206
|
+
*/
|
|
207
|
+
Mysql.prototype.run = async function (sql, params = []) {
|
|
208
|
+
// 参数校验
|
|
209
|
+
if (typeof sql !== 'string' || sql.trim() === '') {
|
|
210
|
+
throw new TypeError('sql must be non-empty string');
|
|
211
|
+
}
|
|
212
|
+
if (!Array.isArray(params)) {
|
|
213
|
+
throw new TypeError('params must be array');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
this.error = null;
|
|
217
|
+
let conn = null;
|
|
218
|
+
let is_pool_conn = false;
|
|
219
|
+
let connection_released = false;
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
// 获取连接
|
|
223
|
+
conn = await this.getConn();
|
|
224
|
+
is_pool_conn = this._usePool;
|
|
225
|
+
|
|
226
|
+
// 直接使用mysql2的query方法
|
|
227
|
+
const [rows] = await conn.query(sql, params);
|
|
228
|
+
return rows;
|
|
229
|
+
} catch (err) {
|
|
230
|
+
this.sql = err.sql;
|
|
231
|
+
this.error = {
|
|
232
|
+
code: err.errno,
|
|
233
|
+
message: err.sqlMessage
|
|
234
|
+
};
|
|
235
|
+
// 在错误情况下也要确保连接释放
|
|
236
|
+
try {
|
|
237
|
+
await safeReleaseConnection(conn, is_pool_conn);
|
|
238
|
+
} catch (release_err) {
|
|
239
|
+
this.log('error', '释放连接失败', release_err);
|
|
240
|
+
}
|
|
241
|
+
connection_released = true;
|
|
242
|
+
} finally {
|
|
243
|
+
// 确保连接被释放,避免资源泄漏
|
|
244
|
+
if (!connection_released) {
|
|
245
|
+
try {
|
|
246
|
+
await safeReleaseConnection(conn, is_pool_conn);
|
|
247
|
+
} catch (release_err) {
|
|
248
|
+
this.log('error', '释放连接失败', release_err);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return [];
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 执行SQL语句(改进版)
|
|
257
|
+
* @param {string} sql - SQL语句
|
|
258
|
+
* @param {Array} params - 参数数组
|
|
259
|
+
* @returns {Promise<object>}
|
|
260
|
+
* @throws {TypeError} 当sql参数无效时
|
|
261
|
+
*/
|
|
262
|
+
Mysql.prototype.exec = async function (sql, params = []) {
|
|
263
|
+
// 参数校验
|
|
264
|
+
if (typeof sql !== 'string' || sql.trim() === '') {
|
|
265
|
+
throw new TypeError('sql must be non-empty string');
|
|
266
|
+
}
|
|
267
|
+
if (!Array.isArray(params)) {
|
|
268
|
+
throw new TypeError('params must be array');
|
|
269
|
+
}
|
|
270
|
+
this.error = null;
|
|
271
|
+
let conn = null;
|
|
272
|
+
let is_pool_conn = false;
|
|
273
|
+
let connection_released = false;
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
// 获取连接
|
|
277
|
+
conn = await this.getConn();
|
|
278
|
+
is_pool_conn = this._usePool;
|
|
279
|
+
|
|
280
|
+
// 直接使用mysql2的execute方法
|
|
281
|
+
const [result] = await conn.execute(sql, params);
|
|
282
|
+
return result.insertId || result.affectedRows || result.changedRows || 1;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
this.log('error', 'SQL执行失败', error);
|
|
285
|
+
this.sql = error.sql;
|
|
286
|
+
this.error = {
|
|
287
|
+
code: error.errno,
|
|
288
|
+
message: error.sqlMessage
|
|
289
|
+
};
|
|
290
|
+
// 在错误情况下也要确保连接释放
|
|
291
|
+
try {
|
|
292
|
+
await safeReleaseConnection(conn, is_pool_conn);
|
|
293
|
+
} catch (release_err) {
|
|
294
|
+
this.log('error', '释放连接失败', release_err);
|
|
295
|
+
}
|
|
296
|
+
connection_released = true;
|
|
297
|
+
} finally {
|
|
298
|
+
// 确保连接被释放,避免资源泄漏
|
|
299
|
+
if (!connection_released) {
|
|
300
|
+
try {
|
|
301
|
+
await safeReleaseConnection(conn, is_pool_conn);
|
|
302
|
+
} catch (release_err) {
|
|
303
|
+
this.log('error', '释放连接失败', release_err);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return 0;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 开始事务(改进版)
|
|
312
|
+
* @returns {Promise<object>} 事务连接对象
|
|
313
|
+
*/
|
|
314
|
+
Mysql.prototype.beginTransaction = async function () {
|
|
315
|
+
let conn = null;
|
|
316
|
+
let transaction_committed = false;
|
|
317
|
+
let transaction_rolled_back = false;
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
conn = await this.getConn();
|
|
321
|
+
await conn.beginTransaction();
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
connection: conn,
|
|
325
|
+
commit: async () => {
|
|
326
|
+
if (transaction_committed || transaction_rolled_back) {
|
|
327
|
+
this.log('warn', '事务已结束,无需重复提交');
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
await conn.commit();
|
|
331
|
+
transaction_committed = true;
|
|
332
|
+
try {
|
|
333
|
+
await safeReleaseConnection(conn, this._usePool);
|
|
334
|
+
} catch (release_err) {
|
|
335
|
+
this.log('error', '提交时释放连接失败', release_err);
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
rollback: async () => {
|
|
339
|
+
if (transaction_committed || transaction_rolled_back) {
|
|
340
|
+
this.log('warn', '事务已结束,无需重复回滚');
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
await conn.rollback();
|
|
344
|
+
transaction_rolled_back = true;
|
|
345
|
+
try {
|
|
346
|
+
await safeReleaseConnection(conn, this._usePool);
|
|
347
|
+
} catch (release_err) {
|
|
348
|
+
this.log('error', '回滚时释放连接失败', release_err);
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
_is_ended: () => transaction_committed || transaction_rolled_back
|
|
352
|
+
};
|
|
353
|
+
} catch (error) {
|
|
354
|
+
// 如果事务开始失败,确保连接被释放
|
|
355
|
+
try {
|
|
356
|
+
await safeReleaseConnection(conn, this._usePool);
|
|
357
|
+
} catch (release_err) {
|
|
358
|
+
this.log('error', '释放连接失败', release_err);
|
|
359
|
+
}
|
|
360
|
+
this.log('error', '事务开始失败', error);
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* 在事务中执行多个操作(改进版)
|
|
367
|
+
* @param {Function} callback - 包含事务操作的回调函数
|
|
368
|
+
* @returns {Promise<*>} 回调函数的返回值
|
|
369
|
+
* @throws {TypeError} 当callback参数无效时
|
|
370
|
+
*/
|
|
371
|
+
Mysql.prototype.transaction = async function (callback) {
|
|
372
|
+
// 参数校验
|
|
373
|
+
if (typeof callback !== 'function') {
|
|
374
|
+
throw new TypeError('callback must be function');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
let transaction = null;
|
|
378
|
+
let transaction_executed = false;
|
|
379
|
+
|
|
380
|
+
try {
|
|
381
|
+
transaction = await this.beginTransaction();
|
|
382
|
+
const result = await callback(transaction);
|
|
383
|
+
|
|
384
|
+
// 检查事务是否已由用户手动提交/回滚
|
|
385
|
+
if (transaction._is_ended && !transaction._is_ended()) {
|
|
386
|
+
await transaction.commit();
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
transaction_executed = true;
|
|
390
|
+
return result;
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (transaction && !transaction_executed) {
|
|
393
|
+
// 只有在事务未成功执行的情况下才回滚
|
|
394
|
+
if (transaction._is_ended && !transaction._is_ended()) {
|
|
395
|
+
await transaction.rollback().catch(err => {
|
|
396
|
+
this.log('error', '事务回滚失败', err);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
this.log('error', '事务执行失败', error);
|
|
402
|
+
throw error; // 重新抛出错误,让调用方知道事务失败
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* 读取整张表的数据(改进版)
|
|
408
|
+
* @param {string} table - 表名
|
|
409
|
+
* @param {object} condition - 查询条件
|
|
410
|
+
* @param {object} options - 选项(orderBy, limit, offset等)
|
|
411
|
+
* @returns {Promise<Array>}
|
|
412
|
+
* @throws {TypeError} 当table参数无效时
|
|
413
|
+
*/
|
|
414
|
+
Mysql.prototype.read = async function (table, condition = {}, options = {}) {
|
|
415
|
+
// 参数校验
|
|
416
|
+
if (typeof table !== 'string' || table.trim() === '') {
|
|
417
|
+
throw new TypeError('table must be non-empty string');
|
|
418
|
+
}
|
|
419
|
+
if (condition && typeof condition !== 'object') {
|
|
420
|
+
throw new TypeError('condition must be object');
|
|
421
|
+
}
|
|
422
|
+
if (options && typeof options !== 'object') {
|
|
423
|
+
throw new TypeError('options must be object');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
try {
|
|
427
|
+
let sql = `SELECT * FROM ${table}`;
|
|
428
|
+
const params = [];
|
|
429
|
+
|
|
430
|
+
// 处理条件
|
|
431
|
+
if (Object.keys(condition).length > 0) {
|
|
432
|
+
const whereClauses = [];
|
|
433
|
+
for (const [field, value] of Object.entries(condition)) {
|
|
434
|
+
whereClauses.push(`${field} = ?`);
|
|
435
|
+
params.push(value);
|
|
436
|
+
}
|
|
437
|
+
sql += ` WHERE ${whereClauses.join(' AND ')}`;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// 处理排序
|
|
441
|
+
if (options.orderBy) {
|
|
442
|
+
sql += ` ORDER BY ${options.orderBy}`;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// 处理分页
|
|
446
|
+
if (options.limit) {
|
|
447
|
+
sql += ` LIMIT ${options.limit}`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (options.offset) {
|
|
451
|
+
sql += ` OFFSET ${options.offset}`;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// 执行查询
|
|
455
|
+
return await this.run(sql, params);
|
|
456
|
+
} catch (error) {
|
|
457
|
+
this.log('error', '查询失败', error);
|
|
458
|
+
throw error; // 重新抛出错误,让调用方知道查询失败
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* 获取连接池状态信息
|
|
464
|
+
* @returns {object} 连接池状态对象
|
|
465
|
+
*/
|
|
466
|
+
Mysql.prototype.getPoolStats = function () {
|
|
467
|
+
if (!this._pool) {
|
|
468
|
+
return {
|
|
469
|
+
status: 'not_initialized',
|
|
470
|
+
message: '连接池未初始化'
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return {
|
|
475
|
+
status: 'healthy'
|
|
476
|
+
};
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* 健康检查
|
|
481
|
+
* @returns {Promise<object>} 健康检查结果
|
|
482
|
+
*/
|
|
483
|
+
Mysql.prototype.healthCheck = async function () {
|
|
484
|
+
try {
|
|
485
|
+
if (!this._pool && !this._connection) {
|
|
486
|
+
return {
|
|
487
|
+
status: 'error',
|
|
488
|
+
message: '数据库连接未初始化'
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// 执行简单的查询来检查连接状态
|
|
493
|
+
const result = await this.run('SELECT 1 as health_check');
|
|
494
|
+
|
|
495
|
+
if (result && result.length > 0 && result[0].health_check === 1) {
|
|
496
|
+
return {
|
|
497
|
+
status: 'healthy',
|
|
498
|
+
message: '数据库连接正常',
|
|
499
|
+
...this.getPoolStats()
|
|
500
|
+
};
|
|
501
|
+
} else {
|
|
502
|
+
return {
|
|
503
|
+
status: 'error',
|
|
504
|
+
message: '健康检查查询失败'
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
} catch (error) {
|
|
508
|
+
this.log('error', '健康检查失败', error);
|
|
509
|
+
return {
|
|
510
|
+
status: 'error',
|
|
511
|
+
message: `健康检查失败: ${error.message}`
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* 获取数据库管理器(保持兼容性)
|
|
518
|
+
* @returns {object} DB实例
|
|
519
|
+
*/
|
|
520
|
+
Mysql.prototype.db = function () {
|
|
521
|
+
return new DB(this);
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* 初始化MySQL服务(保持兼容性)
|
|
526
|
+
* @returns {Promise<void>}
|
|
527
|
+
*/
|
|
528
|
+
Mysql.prototype.init = async function () {
|
|
529
|
+
await this.open();
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* 导出模块
|
|
534
|
+
*/
|
|
535
|
+
exports.Mysql = Mysql;
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* 确保连接池对象存在
|
|
539
|
+
*/
|
|
540
|
+
if (!$.pool) {
|
|
541
|
+
$.pool = {};
|
|
542
|
+
}
|
|
543
|
+
if (!$.pool.mysql) {
|
|
544
|
+
$.pool.mysql = {};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* 全局锁对象,用于线程安全
|
|
549
|
+
*/
|
|
550
|
+
if (!$.pool.mysql._locks) {
|
|
551
|
+
$.pool.mysql._locks = {};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* 获取锁
|
|
556
|
+
* @param {string} key - 锁的键名
|
|
557
|
+
* @returns {Promise<Function>} 释放锁的函数
|
|
558
|
+
*/
|
|
559
|
+
function _acquireLock(key) {
|
|
560
|
+
if (!$.pool.mysql._locks[key]) {
|
|
561
|
+
$.pool.mysql._locks[key] = Promise.resolve();
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
let release;
|
|
565
|
+
const lockPromise = $.pool.mysql._locks[key].then(() => {
|
|
566
|
+
return new Promise(resolve => {
|
|
567
|
+
release = resolve;
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
$.pool.mysql._locks[key] = lockPromise;
|
|
572
|
+
return lockPromise.then(() => release);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* @description Mysql管理器,用于创建缓存(线程安全版)
|
|
577
|
+
* @param {string} scope 作用域
|
|
578
|
+
* @param {object} config 配置参数
|
|
579
|
+
* @returns {object} 返回一个Mysql类实例
|
|
580
|
+
*/
|
|
581
|
+
function mysqlAdmin(scope, config) {
|
|
582
|
+
if (!scope) {
|
|
583
|
+
scope = 'sys';
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// 检查是否已存在实例
|
|
587
|
+
if ($.pool.mysql[scope]) {
|
|
588
|
+
return $.pool.mysql[scope];
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// 同步方式创建实例(无需锁,因为Node.js是单线程的)
|
|
592
|
+
if (!$.pool.mysql[scope]) {
|
|
593
|
+
$.pool.mysql[scope] = new Mysql(config);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return $.pool.mysql[scope];
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* @module 导出Mysql管理器
|
|
601
|
+
*/
|
|
602
602
|
exports.mysqlAdmin = mysqlAdmin;
|