jj.js 0.18.0 → 0.20.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.
@@ -0,0 +1,294 @@
1
+ const Sql = require('./sql');
2
+
3
+ /**
4
+ * @typedef {import('../../types').Pool} Pool
5
+ * @typedef {import('../../types').PoolConfig} PoolConfig
6
+ * @typedef {import('../../types').PoolConnection} PoolConnection
7
+ * @typedef {import('../../types').FieldInfo} FieldInfo
8
+ * @typedef {Map<any, Pool>} PoolMap
9
+ */
10
+
11
+ //连接池
12
+ /**
13
+ * @type {PoolMap}
14
+ */
15
+ const pool = new Map();
16
+ //事务连接
17
+ const trans = new Map();
18
+ //事务嵌套
19
+ const nest = new Map();
20
+
21
+ /**
22
+ * @extends Sql
23
+ */
24
+ class Mysql extends Sql
25
+ {
26
+ /**
27
+ * 连接数据库连接池
28
+ * @public
29
+ * @param {PoolConfig} config - 数据库配置标识或连接参数
30
+ * @returns {Promise<this>}
31
+ }
32
+ */
33
+ async connect(config={}) {
34
+ this._config = config;
35
+
36
+ if(!pool.has(this._config)) {
37
+ const cur_pool = require('mysql').createPool(this._config);
38
+ pool.set(this._config, cur_pool);
39
+ this.logger.sql(`MYSQL连接池创建成功:{all: ${pool.size}}`);
40
+ }
41
+
42
+ return this;
43
+ }
44
+
45
+ /**
46
+ * 关闭数据库连接池
47
+ * @public
48
+ * @returns {Promise<this>}
49
+ */
50
+ async close() {
51
+ const that = this;
52
+ return new Promise((resolve, reject) => {
53
+ if(pool.has(this._config)) {
54
+ // @ts-ignore
55
+ pool.get(this._config).end(err => {
56
+ if(err) {
57
+ const message = 'MYSQL连接池销毁失败:' + err.message;
58
+ that.logger.sql(message);
59
+ that.logger.error(message);
60
+ reject(new Error('DbError: ' + message));
61
+ } else {
62
+ pool.delete(this._config);
63
+ that.logger.sql(`MYSQL连接池销毁成功:{poolTotal: ${pool.size}}`);
64
+ resolve(that);
65
+ }
66
+ });
67
+ } else {
68
+ resolve(that);
69
+ }
70
+ });
71
+ }
72
+
73
+ /**
74
+ * 释放数据库连接
75
+ * @public
76
+ * @param {PoolConnection} conn - 数据库连接
77
+ */
78
+ release(conn) {
79
+ try {
80
+ conn.release();
81
+ this.logger.sql('释放数据库连接!');
82
+ } catch(e) {
83
+ const message = '释放数据库出错:' + (e instanceof Error && e.message || String(e));
84
+ this.logger.sql(message);
85
+ this.logger.error(message);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * 获取数据库连接
91
+ * @private
92
+ * @param {Pool} p
93
+ * @returns {Promise<PoolConnection>}
94
+ */
95
+ async _creatConnect(p) {
96
+ return new Promise((resolve, reject) => {
97
+ p.getConnection((err, connection) => {
98
+ if(err) {
99
+ const message = '获取数据库连接失败:' + err.message;
100
+ this.logger.sql(message);
101
+ this.logger.error(message);
102
+ reject(new Error('DbError: ' + message));
103
+ } else {
104
+ // @ts-ignore
105
+ this.logger.sql(`获取数据库连接:{limit: ${p.config.connectionLimit}, all: ${p._allConnections.length}, acquiring: ${p._acquiringConnections.length}, free: ${p._freeConnections.length}, queue: ${p._connectionQueue.length}}`);
106
+ resolve(connection);
107
+ }
108
+ });
109
+ });
110
+ }
111
+
112
+ /**
113
+ * 获取数据库连接
114
+ * @private
115
+ * @returns {Promise<PoolConnection>}
116
+ */
117
+ async _getConnect() {
118
+ // @ts-ignore
119
+ return trans.get(this.ctx) || await this._creatConnect(pool.get(this._config));
120
+ }
121
+
122
+ /**
123
+ * 开启事务
124
+ * @public
125
+ * @param {function} fun
126
+ * @returns {Promise<string>}
127
+ */
128
+ async startTrans(fun) {
129
+ const conn = await this._getConnect();
130
+ trans.set(this.ctx, conn);
131
+ let trans_nest = nest.get(conn) || 0;
132
+ nest.set(conn, ++trans_nest);
133
+ if(trans_nest > 1) {
134
+ const message = `开启事务成功:{nest:${trans_nest}}`;
135
+ this.logger.sql(message);
136
+ if(typeof fun === 'function') {
137
+ try {
138
+ await fun();
139
+ await this.commit();
140
+ } catch(e) {
141
+ await this.rollback();
142
+ throw e;
143
+ }
144
+ }
145
+ return message;
146
+ }
147
+
148
+ return new Promise((resolve, reject) => {
149
+ conn.beginTransaction(async err => {
150
+ if(err) {
151
+ const message = '开启事务失败:' + err.message;
152
+ this.logger.sql(message);
153
+ this.logger.error(message);
154
+ reject(new Error('DbError: ' + message));
155
+ } else {
156
+ const message = '开启事务成功!';
157
+ this.logger.sql(message);
158
+ if(typeof fun === 'function') {
159
+ try {
160
+ await fun();
161
+ await this.commit();
162
+ resolve('事务执行成功!');
163
+ } catch(e) {
164
+ await this.rollback();
165
+ reject(e);
166
+ }
167
+ }
168
+ resolve(message);
169
+ }
170
+ });
171
+ });
172
+ }
173
+
174
+ /**
175
+ * 事务回滚
176
+ * @public
177
+ * @returns {Promise<string>}
178
+ */
179
+ async rollback() {
180
+ const conn = await this._getConnect();
181
+ const trans_nest = nest.get(conn) || 0;
182
+ if(trans_nest > 1) {
183
+ nest.set(conn, trans_nest - 1);
184
+ const message = `事务回滚成功:{nest:${trans_nest}}`;
185
+ this.logger.sql(message);
186
+ return message;
187
+ } else {
188
+ nest.delete(conn);
189
+ }
190
+
191
+ return new Promise((resolve, reject) => {
192
+ conn.rollback(err => {
193
+ if(err) {
194
+ reject(new Error('DbError: ' + err.message));
195
+ } else {
196
+ trans.delete(this.ctx);
197
+ const message = '事务回滚成功!';
198
+ this.logger.sql(message);
199
+ this.release(conn);
200
+ resolve(message);
201
+ }
202
+ });
203
+ });
204
+ }
205
+
206
+ /**
207
+ * 提交事务
208
+ * @public
209
+ * @returns {Promise<string>}
210
+ */
211
+ async commit() {
212
+ const conn = await this._getConnect();
213
+ const trans_nest = nest.get(conn) || 0;
214
+ if(trans_nest > 1) {
215
+ nest.set(conn, trans_nest - 1);
216
+ const message = `事务提交成功:{nest:${trans_nest}}`;
217
+ this.logger.sql(message);
218
+ return message;
219
+ } else {
220
+ nest.delete(conn);
221
+ }
222
+
223
+ return new Promise((resolve, reject) => {
224
+ conn.commit(err => {
225
+ if(err) {
226
+ const message = '事务提交失败:' + err.message;
227
+ this.logger.sql(message);
228
+ this.logger.error(message);
229
+ this.rollback();
230
+ reject(new Error('DbError: ' + message));
231
+ } else {
232
+ trans.delete(this.ctx);
233
+ const message = '事务提交成功!';
234
+ this.logger.sql(message);
235
+ this.release(conn);
236
+ resolve(message);
237
+ }
238
+ });
239
+ });
240
+ }
241
+
242
+ /**
243
+ * 执行sql查询
244
+ * @public
245
+ * @param {string} sql - sql语句或参数
246
+ * @param {*} params - sql参数
247
+ * @returns {Promise<*>}
248
+ */
249
+ async query(sql, params) {
250
+ params || (params = []);
251
+ const conn = await this._getConnect();
252
+
253
+ return new Promise((resolve, reject) => {
254
+ conn.query(sql, params, (err, data) => {
255
+ if(err) {
256
+ const message = '数据操作失败,' + err.message + "\nSQL:" + this.format(sql, params);
257
+ this.logger.sql(message);
258
+ this.logger.error(message);
259
+ reject(new Error('DbError: ' + message));
260
+ } else {
261
+ this.logger.sql('数据操作成功,SQL:' + this.format(sql, params));
262
+ resolve(data);
263
+ }
264
+ if(!trans.has(this.ctx)) {
265
+ this.release(conn);
266
+ }
267
+ });
268
+ });
269
+ }
270
+
271
+ /**
272
+ * 序列化sql语句
273
+ * @public
274
+ * @param {string} sql - sql语句或参数
275
+ * @param {*} params - sql参数
276
+ * @returns {string}
277
+ */
278
+ format(sql, params) {
279
+ params || (params = []);
280
+ return require('mysql').format(sql, params);
281
+ }
282
+
283
+ /**
284
+ * 获取数据表信息
285
+ * @public
286
+ * @param {string} tableName - 表名字
287
+ * @returns {Promise<FieldInfo[]>}
288
+ */
289
+ async tableInfo(tableName) {
290
+ return await this.query(`show columns from ?`, [tableName]);
291
+ }
292
+ }
293
+
294
+ module.exports = Mysql;
package/lib/db/sql.js ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @typedef {import('../../types').Logger} Logger
3
+ * @typedef {import('../../types').FieldInfo} FieldInfo
4
+ */
5
+
6
+ class Sql
7
+ {
8
+ /**
9
+ * @public
10
+ * @param {object} ctx
11
+ * @param {Logger} logger
12
+ */
13
+ constructor(ctx, logger) {
14
+ this.ctx = ctx;
15
+ this.logger = logger;
16
+ }
17
+
18
+ /**
19
+ * 连接数据库
20
+ * @public
21
+ * @param {object} config - 数据库连接参数
22
+ * @returns {Promise<this>}
23
+ */
24
+ async connect(config={}) {
25
+ return this;
26
+ }
27
+
28
+ /**
29
+ * 关闭数据库
30
+ * @public
31
+ * @returns {Promise<this>}
32
+ */
33
+ async close() {
34
+ return this;
35
+ }
36
+
37
+ /**
38
+ * 释放数据库连接
39
+ * @public
40
+ * @param {object} conn - 数据库连接
41
+ */
42
+ release(conn) {
43
+ }
44
+
45
+ /**
46
+ * 开启事务
47
+ * @public
48
+ * @param {function} [fun]
49
+ * @returns {Promise<string>}
50
+ */
51
+ async startTrans(fun) {
52
+ return '开启事务成功';
53
+ }
54
+
55
+ /**
56
+ * 事务回滚
57
+ * @public
58
+ * @returns {Promise<string>}
59
+ */
60
+ async rollback() {
61
+ return '事务回滚成功';
62
+ }
63
+
64
+ /**
65
+ * 提交事务
66
+ * @public
67
+ * @returns {Promise<string>}
68
+ */
69
+ async commit() {
70
+ return '事务提交成功';
71
+ }
72
+
73
+ /**
74
+ * 执行sql查询
75
+ * @public
76
+ * @param {string} sql - sql语句或参数
77
+ * @param {*} params - sql参数
78
+ * @returns {Promise<*>}
79
+ */
80
+ async query(sql, params) {
81
+ }
82
+
83
+ /**
84
+ * 序列化sql语句
85
+ * @public
86
+ * @param {string} sql - sql语句或参数
87
+ * @param {*} params - sql参数
88
+ * @returns {string}
89
+ */
90
+ format(sql, params) {
91
+ params || (params = []);
92
+ return `${sql} ${params.map((/** @type {any[]} */ item) => item.join('=')).join(', ')}`;
93
+ }
94
+
95
+ /**
96
+ * 获取数据表信息
97
+ * @public
98
+ * @param {string} tableName - 表名字
99
+ * @returns {Promise<FieldInfo[]>}
100
+ */
101
+ async tableInfo(tableName) {
102
+ return await this.query(`show columns from ?`, [tableName]);
103
+ }
104
+
105
+ /**
106
+ * 设置日志器
107
+ * @param {Logger} logger
108
+ * @returns {this}
109
+ */
110
+ setLogger(logger) {
111
+ this.logger = logger;
112
+ return this;
113
+ }
114
+ }
115
+
116
+ module.exports = Sql;
@@ -0,0 +1,292 @@
1
+ const {app: cfg_app} = require('../config');
2
+ const Sql = require('./sql');
3
+
4
+ /**
5
+ * @typedef {import('../../types').DbConfigItem} DbConfigItem
6
+ * @typedef {import('sqlite3').Database} Database
7
+ * @typedef {import('../../types').FieldInfo} FieldInfo
8
+ * @typedef {Map<any, Database>} ConnectionMap
9
+ */
10
+
11
+ //数据库连接
12
+ /**
13
+ * @type {ConnectionMap}
14
+ */
15
+ const connections = new Map();
16
+ //事务连接
17
+ const trans = new Map();
18
+ //事务嵌套
19
+ const nest = new Map();
20
+
21
+ /**
22
+ * @extends Sql
23
+ */
24
+ class Sqlite extends Sql
25
+ {
26
+ /**
27
+ * 连接数据库
28
+ * @public
29
+ * @param {DbConfigItem} config - 数据库配置标识或连接参数
30
+ * @returns {Promise<this>}
31
+ */
32
+ async connect(config) {
33
+ this._config = config;
34
+
35
+ let connection = connections.get(this._config);
36
+ if(!connection) {
37
+ const sqlite3 = cfg_app.app_debug ? require('sqlite3').verbose() : require('sqlite3');
38
+ connection = new sqlite3.Database(this._config.database);
39
+ connections.set(this._config, connection);
40
+ this.logger.sql(`SQLITE数据库连接创建成功:{all: ${connections.size}}`);
41
+ }
42
+
43
+ return this;
44
+ }
45
+
46
+ /**
47
+ * 关闭数据库连接
48
+ * @public
49
+ * @returns {Promise<this>}
50
+ */
51
+ async close() {
52
+ const that = this;
53
+ return new Promise((resolve, reject) => {
54
+ if(connections.has(this._config)) {
55
+ const connection = connections.get(this._config);
56
+ // @ts-ignore
57
+ connection.close(err => {
58
+ if(err) {
59
+ const message = 'SQLITE数据库连接关闭失败:' + err.message;
60
+ this.logger.sql(message);
61
+ this.logger.error(message);
62
+ reject(new Error('DbError: ' + message));
63
+ } else {
64
+ connections.delete(this._config);
65
+ this.logger.sql(`SQLITE数据库连接关闭成功:{connectionTotal: ${connections.size}}`);
66
+ resolve(that);
67
+
68
+ }
69
+ });
70
+ } else {
71
+ resolve(that);
72
+ }
73
+ });
74
+ }
75
+
76
+ /**
77
+ * 获取数据库连接
78
+ * @private
79
+ * @returns {Promise<Database>}
80
+ */
81
+ async _getConnect() {
82
+ return trans.get(this.ctx) || connections.get(this._config);
83
+ }
84
+
85
+ /**
86
+ * 开启事务
87
+ * @public
88
+ * @param {function} fun
89
+ * @returns {Promise<string>}
90
+ */
91
+ async startTrans(fun) {
92
+ const conn = await this._getConnect();
93
+ trans.set(this.ctx, conn);
94
+ let trans_nest = nest.get(conn) || 0;
95
+ nest.set(conn, ++trans_nest);
96
+ if(trans_nest > 1) {
97
+ const message = `开启事务成功:{nest:${trans_nest}}`;
98
+ this.logger.sql(message);
99
+ if(typeof fun === 'function') {
100
+ try {
101
+ await fun();
102
+ await this.commit();
103
+ } catch(e) {
104
+ await this.rollback();
105
+ throw e;
106
+ }
107
+ }
108
+ return message;
109
+ }
110
+
111
+ return new Promise((resolve, reject) => {
112
+ conn.run('BEGIN TRANSACTION;', async err => {
113
+ if(err) {
114
+ const message = '开启事务失败:' + err.message;
115
+ this.logger.sql(message);
116
+ this.logger.error(message);
117
+ reject(new Error('DbError: ' + message));
118
+ } else {
119
+ const message = '开启事务成功!';
120
+ this.logger.sql(message);
121
+ if(typeof fun === 'function') {
122
+ try {
123
+ await fun();
124
+ await this.commit();
125
+ resolve('事务执行成功!');
126
+ } catch(e) {
127
+ await this.rollback();
128
+ reject(e);
129
+ }
130
+ }
131
+ resolve(message);
132
+ }
133
+ });
134
+ });
135
+ }
136
+
137
+ /**
138
+ * 事务回滚
139
+ * @public
140
+ * @returns {Promise<string>}
141
+ */
142
+ async rollback() {
143
+ const conn = await this._getConnect();
144
+ const trans_nest = nest.get(conn) || 0;
145
+ if(trans_nest > 1) {
146
+ nest.set(conn, trans_nest - 1);
147
+ const message = `事务回滚成功:{nest:${trans_nest}}`;
148
+ this.logger.sql(message);
149
+ return message;
150
+ } else {
151
+ nest.delete(conn);
152
+ }
153
+
154
+ return new Promise((resolve, reject) => {
155
+ conn.run('ROLLBACK;', err => {
156
+ if(err) {
157
+ reject(new Error('DbError: ' + err.message));
158
+ } else {
159
+ trans.delete(this.ctx);
160
+ const message = '事务回滚成功!';
161
+ this.logger.sql(message);
162
+ resolve(message);
163
+ }
164
+ });
165
+ });
166
+ }
167
+
168
+ /**
169
+ * 提交事务
170
+ * @public
171
+ * @returns {Promise<string>}
172
+ */
173
+ async commit() {
174
+ const conn = await this._getConnect();
175
+ const trans_nest = nest.get(conn) || 0;
176
+ if(trans_nest > 1) {
177
+ nest.set(conn, trans_nest - 1);
178
+ const message = `事务提交成功:{nest:${trans_nest}}`;
179
+ this.logger.sql(message);
180
+ return message;
181
+ } else {
182
+ nest.delete(conn);
183
+ }
184
+
185
+ return new Promise((resolve, reject) => {
186
+ conn.run('COMMIT;', err => {
187
+ if(err) {
188
+ const message = '事务提交失败:' + err.message;
189
+ this.logger.sql(message);
190
+ this.logger.error(message);
191
+ this.rollback();
192
+ reject(new Error('DbError: ' + message));
193
+ } else {
194
+ trans.delete(this.ctx);
195
+ const message = '事务提交成功!';
196
+ this.logger.sql(message);
197
+ resolve(message);
198
+ }
199
+ });
200
+ });
201
+ }
202
+
203
+ /**
204
+ * 执行sql查询
205
+ * @public
206
+ * @param {string} sql - sql语句或参数
207
+ * @param {*} [params] - sql参数
208
+ * @returns {Promise<*>}
209
+ */
210
+ async query(sql, params) {
211
+ params || (params = []);
212
+ const conn = await this._getConnect();
213
+
214
+ return new Promise((resolve, reject) => {
215
+ // 判断是SELECT查询还是其他操作
216
+ const isSelect = /^\s*select /i.test(sql);
217
+ const isTableInfo = /^\s*PRAGMA table_info/i.test(sql);
218
+
219
+ const that = this;
220
+ // @ts-ignore
221
+ conn[isSelect || isTableInfo ? 'all' : 'run'](sql, params, function(err, rows) {
222
+ if(err) {
223
+ const message = '数据操作失败,' + err.message + "\nSQL:" + that.format(sql, params);
224
+ that.logger.sql(message);
225
+ that.logger.error(message);
226
+ reject(new Error('DbError: ' + message));
227
+ } else {
228
+ that.logger.sql('数据操作成功,SQL:' + that.format(sql, params));
229
+ if(isSelect || isTableInfo) {
230
+ resolve(rows);
231
+ } else {
232
+ resolve({
233
+ // @ts-ignore - sqlite3 run 回调中的 this 包含 changes 和 lastID
234
+ affectedRows: this.changes,
235
+ // @ts-ignore - sqlite3 run 回调中的 this 包含 changes 和 lastID
236
+ insertId: this.lastID
237
+ });
238
+ }
239
+ }
240
+ if(!trans.has(that.ctx)) {
241
+ // SQLite不需要显式释放连接
242
+ }
243
+ });
244
+ });
245
+ }
246
+
247
+ /**
248
+ * 序列化sql语句
249
+ * @public
250
+ * @param {string} sql - sql语句或参数
251
+ * @param {*} params - sql参数
252
+ * @returns {string}
253
+ */
254
+ format(sql, params) {
255
+ params || (params = []);
256
+
257
+ let formatted = sql;
258
+ for(let i = 0; i < params.length; i++) {
259
+ let value = params[i];
260
+ if (typeof value === 'string') {
261
+ value = "'" + value.replace(/'/g, "''") + "'";
262
+ } else if (value === null) {
263
+ value = 'NULL';
264
+ }
265
+ formatted = formatted.replace(/\?/, value);
266
+ }
267
+ return formatted;
268
+ }
269
+
270
+ /**
271
+ * 获取数据表信息
272
+ * @public
273
+ * @param {string} tableName - 表名字
274
+ * @returns {Promise<FieldInfo[]>}
275
+ */
276
+ async tableInfo(tableName) {
277
+ const columns = await this.query(`PRAGMA table_info(${tableName})`);
278
+ // @ts-ignore
279
+ return columns.map(col => {
280
+ return {
281
+ Field: col.name,
282
+ Type: col.type,
283
+ Null: col.notnull === 1 ? 'NO' : 'YES',
284
+ Key: col.pk === 1 ? 'PRI' : '',
285
+ Default: col.dflt_value,
286
+ Extra: col.pk === 1 ? 'auto_increment' : '',
287
+ };
288
+ });
289
+ }
290
+ }
291
+
292
+ module.exports = Sqlite;