mm_sql 1.3.9 → 1.4.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/README.md +152 -165
- package/db/mm.db +0 -0
- package/index.js +146 -93
- package/package.json +3 -3
- package/test.js +27 -70
- package/test2.js +3 -4
- package//345/205/274/345/256/271/346/200/247/346/212/245/345/221/212.md +0 -202
- package//345/205/274/345/256/271/346/200/247/346/265/213/350/257/225/346/212/245/345/221/212.md +0 -87
- package//351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md +0 -160
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mm_sql
|
|
2
2
|
|
|
3
|
-
一个通用的SQL帮助类,支持通过切换db_type实现对不同数据库的操作,包括MySQL和SQLite等,提供统一的API
|
|
3
|
+
一个通用的SQL帮助类,支持通过切换db_type实现对不同数据库的操作,包括MySQL和SQLite等,提供统一的API接口,支持异步操作和基础SQL执行。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
@@ -23,19 +23,22 @@ const { Sql } = require('mm_sql');
|
|
|
23
23
|
|
|
24
24
|
// 创建实例(默认使用MySQL)
|
|
25
25
|
const sql = new Sql({
|
|
26
|
+
db_type: 'mysql',
|
|
26
27
|
host: 'localhost',
|
|
27
28
|
port: 3306,
|
|
28
29
|
user: 'root',
|
|
29
30
|
password: 'password',
|
|
30
|
-
database: 'test_db'
|
|
31
|
-
table: 'users'
|
|
31
|
+
database: 'test_db'
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
const users = await sql.
|
|
34
|
+
// 执行查询SQL
|
|
35
|
+
const users = await sql.run('SELECT * FROM users WHERE status = 1');
|
|
36
|
+
|
|
37
|
+
// 执行更新SQL
|
|
38
|
+
const result = await sql.exec('UPDATE users SET status = 0 WHERE id = 1');
|
|
36
39
|
|
|
37
|
-
//
|
|
38
|
-
await sql.
|
|
40
|
+
// 关闭连接
|
|
41
|
+
await sql.close();
|
|
39
42
|
```
|
|
40
43
|
|
|
41
44
|
### 2. 切换数据库类型
|
|
@@ -47,135 +50,95 @@ const sql = new Sql({
|
|
|
47
50
|
host: 'localhost',
|
|
48
51
|
user: 'root',
|
|
49
52
|
password: 'password',
|
|
50
|
-
database: 'mysql_db'
|
|
51
|
-
table: 'users'
|
|
53
|
+
database: 'mysql_db'
|
|
52
54
|
});
|
|
53
55
|
|
|
54
56
|
// 切换到SQLite
|
|
55
|
-
|
|
57
|
+
sql.setConfig({
|
|
58
|
+
db_type: 'sqlite',
|
|
56
59
|
dir: '/db/',
|
|
57
|
-
database: 'sqlite.db'
|
|
58
|
-
table: 'users'
|
|
60
|
+
database: 'sqlite.db'
|
|
59
61
|
});
|
|
60
62
|
|
|
61
|
-
// 使用SQLite
|
|
62
|
-
const data = await
|
|
63
|
+
// 使用SQLite进行操作
|
|
64
|
+
const data = await sql.run('SELECT * FROM users');
|
|
63
65
|
```
|
|
64
66
|
|
|
65
67
|
### 3. 主要方法
|
|
66
68
|
|
|
67
|
-
####
|
|
69
|
+
#### SQL执行方法
|
|
68
70
|
|
|
69
71
|
```javascript
|
|
70
|
-
//
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
// 查询单条数据
|
|
74
|
-
const user = await sql.getObj({ id: 1 });
|
|
75
|
-
|
|
76
|
-
// 分页查询
|
|
77
|
-
const result = await sql.getCount(
|
|
78
|
-
{ status: 1 },
|
|
79
|
-
'created_at DESC',
|
|
80
|
-
'id, name, email'
|
|
81
|
-
);
|
|
82
|
-
// result结构: { list: [...], total: 100, page: 1, size: 20, pages: 5 }
|
|
83
|
-
|
|
84
|
-
// 统计
|
|
85
|
-
const count = await sql.count({ status: 1 });
|
|
86
|
-
```
|
|
72
|
+
// 执行查询操作(返回结果集)
|
|
73
|
+
const users = await sql.run('SELECT * FROM users WHERE status = 1');
|
|
87
74
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
```javascript
|
|
91
|
-
// 添加数据
|
|
92
|
-
const addResult = await sql.add({
|
|
93
|
-
name: '张三',
|
|
94
|
-
age: 25,
|
|
95
|
-
status: 1
|
|
96
|
-
});
|
|
75
|
+
// 执行带参数的查询
|
|
76
|
+
const user = await sql.run('SELECT * FROM users WHERE id = ?', [1]);
|
|
97
77
|
|
|
98
|
-
//
|
|
99
|
-
const
|
|
100
|
-
{ id: 1 }, // 查询条件
|
|
101
|
-
{ name: '李四', age: 26 } // 更新字段
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
// 使用where属性修改数据
|
|
105
|
-
const updateResult2 = await sql.set({
|
|
106
|
-
where: { id: 1 },
|
|
107
|
-
name: '李四',
|
|
108
|
-
age: 26
|
|
109
|
-
});
|
|
78
|
+
// 执行带超时的查询
|
|
79
|
+
const timeoutResult = await sql.run('SELECT * FROM users WHERE status = 1', [], 5000);
|
|
110
80
|
|
|
111
|
-
//
|
|
112
|
-
const
|
|
81
|
+
// 执行修改操作(返回影响行数)
|
|
82
|
+
const updateResult = await sql.exec('UPDATE users SET status = 0 WHERE id = 1');
|
|
113
83
|
|
|
114
|
-
//
|
|
115
|
-
const
|
|
116
|
-
{ id: 1 }, // 查询条件
|
|
117
|
-
{ name: '王五', age: 27 } // 要设置的字段
|
|
118
|
-
);
|
|
119
|
-
```
|
|
84
|
+
// 执行带参数的修改
|
|
85
|
+
const updateWithParams = await sql.exec('UPDATE users SET name = ? WHERE id = ?', ['张三', 1]);
|
|
120
86
|
|
|
121
|
-
|
|
87
|
+
// 执行带超时的修改
|
|
88
|
+
const timeoutExec = await sql.exec('DELETE FROM users WHERE status = 0', [], 3000);
|
|
122
89
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// 批量修改
|
|
131
|
-
const setListResult = await sql.setList([
|
|
132
|
-
{ query: { id: 1 }, item: { status: 0 } },
|
|
133
|
-
{ query: { id: 2 }, item: { status: 0 } }
|
|
134
|
-
]);
|
|
135
|
-
|
|
136
|
-
// 批量删除
|
|
137
|
-
const delListResult = await sql.delList([
|
|
138
|
-
{ query: { id: 1 } },
|
|
139
|
-
{ query: { id: 2 } }
|
|
140
|
-
]);
|
|
90
|
+
// 使用对象参数方式调用
|
|
91
|
+
const result = await sql.run({
|
|
92
|
+
sql: 'SELECT * FROM users WHERE name LIKE ?',
|
|
93
|
+
params: ['张%'],
|
|
94
|
+
timeout: 5000
|
|
95
|
+
});
|
|
141
96
|
```
|
|
142
97
|
|
|
143
|
-
####
|
|
98
|
+
#### 连接管理方法
|
|
144
99
|
|
|
145
100
|
```javascript
|
|
146
|
-
//
|
|
147
|
-
await sql.
|
|
148
|
-
|
|
149
|
-
// 修改字段
|
|
150
|
-
await sql.setField('phone', 'varchar(11)', '0');
|
|
101
|
+
// 手动打开数据库连接
|
|
102
|
+
await sql.open();
|
|
151
103
|
|
|
152
|
-
//
|
|
153
|
-
await sql.
|
|
104
|
+
// 手动关闭数据库连接
|
|
105
|
+
await sql.close();
|
|
154
106
|
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
107
|
+
// 动态修改配置
|
|
108
|
+
sql.setConfig({
|
|
109
|
+
host: 'new_host',
|
|
110
|
+
database: 'new_database'
|
|
111
|
+
});
|
|
160
112
|
```
|
|
161
113
|
|
|
162
|
-
####
|
|
114
|
+
#### 模板方法
|
|
163
115
|
|
|
164
116
|
```javascript
|
|
165
|
-
//
|
|
166
|
-
const
|
|
117
|
+
// SQL模板查询生成
|
|
118
|
+
const query = sql.tplQuery({ name: '张', age: 20 }, {
|
|
119
|
+
name: '`name` LIKE "{0}%"',
|
|
120
|
+
age: '`age` > {0}'
|
|
121
|
+
});
|
|
122
|
+
// 生成: "`name` LIKE \"张%\" AND `age` > 20"
|
|
167
123
|
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
124
|
+
// SQL模板数据生成
|
|
125
|
+
const body = sql.tplBody({ name: '李四', age: 25 }, {
|
|
126
|
+
name: '`name` = {0}',
|
|
127
|
+
age: '`age` = {0}'
|
|
172
128
|
});
|
|
129
|
+
// 生成: "`name` = '李四', `age` = 25"
|
|
130
|
+
|
|
131
|
+
// 参数过滤
|
|
132
|
+
const filteredParams = sql.filter({ id: 1, name: '张三', password: '123' }, ['password']);
|
|
133
|
+
// 结果: { id: 1, name: '张三' }
|
|
134
|
+
```
|
|
173
135
|
|
|
174
|
-
|
|
175
|
-
await sql.delTable();
|
|
136
|
+
#### 数据库适配器方法
|
|
176
137
|
|
|
177
|
-
|
|
178
|
-
|
|
138
|
+
```javascript
|
|
139
|
+
// 获取底层数据库适配器
|
|
140
|
+
const dbAdapter = sql.db();
|
|
141
|
+
// 可用于直接操作底层数据库接口
|
|
179
142
|
```
|
|
180
143
|
|
|
181
144
|
### 4. 配置选项
|
|
@@ -185,14 +148,8 @@ const sql = new Sql({
|
|
|
185
148
|
// 数据库类型
|
|
186
149
|
db_type: 'mysql', // 可选值: 'mysql', 'sqlite'
|
|
187
150
|
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
key: 'id', // 主键
|
|
191
|
-
|
|
192
|
-
// 分页配置
|
|
193
|
-
page: 1, // 默认页码
|
|
194
|
-
size: 20, // 默认每页数量
|
|
195
|
-
limit: 1000, // 最大查询数量
|
|
151
|
+
// 作用域配置
|
|
152
|
+
scope: 'sys', // 默认作用域
|
|
196
153
|
|
|
197
154
|
// MySQL配置
|
|
198
155
|
host: 'localhost',
|
|
@@ -200,59 +157,13 @@ const sql = new Sql({
|
|
|
200
157
|
user: 'root',
|
|
201
158
|
password: '',
|
|
202
159
|
database: '',
|
|
203
|
-
charset: 'utf8mb4',
|
|
204
|
-
connect_timeout: 10000, // 连接超时时间(毫秒)
|
|
205
|
-
query_timeout: 30000, // 查询超时时间(毫秒)
|
|
206
160
|
|
|
207
161
|
// SQLite配置
|
|
208
162
|
dir: '/db/', // SQLite数据库文件存储目录
|
|
209
|
-
database: 'db.sqlite'
|
|
210
|
-
|
|
211
|
-
// 其他配置
|
|
212
|
-
filter: {}, // 过滤参数
|
|
213
|
-
separator: '|', // 分隔符,用于多条件查询
|
|
214
|
-
way: 'add del set get', // 请求方式
|
|
215
|
-
field_hide: ['*password*', '*token*'] // 隐藏字段
|
|
163
|
+
database: 'db.sqlite' // SQLite数据库文件名
|
|
216
164
|
});
|
|
217
165
|
```
|
|
218
166
|
|
|
219
|
-
## 事务操作
|
|
220
|
-
|
|
221
|
-
```javascript
|
|
222
|
-
// 使用事务方法
|
|
223
|
-
await sql.start(); // 开始事务
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
// 在事务中执行操作
|
|
227
|
-
await sql.add({ name: '测试用户1', age: 30 });
|
|
228
|
-
await sql.add({ name: '测试用户2', age: 25 });
|
|
229
|
-
|
|
230
|
-
await sql.commit(); // 提交事务
|
|
231
|
-
} catch (error) {
|
|
232
|
-
await sql.back(); // 回滚事务
|
|
233
|
-
console.error('事务执行失败:', error);
|
|
234
|
-
} finally {
|
|
235
|
-
await sql.end(); // 结束事务
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// 直接执行事务SQL(MySQL)
|
|
239
|
-
const mysqlTransactionResult = await sql.exec(`
|
|
240
|
-
START TRANSACTION;
|
|
241
|
-
INSERT INTO users (name, age) VALUES ('测试用户', 30);
|
|
242
|
-
UPDATE users SET status = 1 WHERE id = LAST_INSERT_ID();
|
|
243
|
-
COMMIT;
|
|
244
|
-
`);
|
|
245
|
-
|
|
246
|
-
// 直接执行事务SQL(SQLite)
|
|
247
|
-
const sqliteTransactionResult = await sql.exec(`
|
|
248
|
-
BEGIN TRANSACTION;
|
|
249
|
-
INSERT INTO users (name, age) VALUES ('测试用户', 30);
|
|
250
|
-
UPDATE users SET status = 1 WHERE id = last_insert_rowid();
|
|
251
|
-
COMMIT;
|
|
252
|
-
`);
|
|
253
|
-
|
|
254
|
-
// 注意:不同数据库的事务语法和函数可能有所不同,请根据实际使用的数据库类型调整SQL语句
|
|
255
|
-
|
|
256
167
|
## 高级用法
|
|
257
168
|
|
|
258
169
|
### 1. SQL模板
|
|
@@ -307,20 +218,96 @@ const user = await sql.run('SELECT * FROM users WHERE id = ?', [1]);
|
|
|
307
218
|
// 执行带超时的SQL
|
|
308
219
|
const timeoutResult = await sql.run('SELECT * FROM users WHERE status = 1', [], 5000);
|
|
309
220
|
|
|
221
|
+
// 执行事务操作(MySQL)
|
|
222
|
+
const mysqlTransactionResult = await sql.exec(`
|
|
223
|
+
START TRANSACTION;
|
|
224
|
+
INSERT INTO users (name, age) VALUES ('测试用户', 30);
|
|
225
|
+
UPDATE users SET status = 1 WHERE id = LAST_INSERT_ID();
|
|
226
|
+
COMMIT;
|
|
227
|
+
`);
|
|
228
|
+
|
|
229
|
+
// 执行事务操作(SQLite)
|
|
230
|
+
const sqliteTransactionResult = await sql.exec(`
|
|
231
|
+
BEGIN TRANSACTION;
|
|
232
|
+
INSERT INTO users (name, age) VALUES ('测试用户', 30);
|
|
233
|
+
UPDATE users SET status = 1 WHERE id = last_insert_rowid();
|
|
234
|
+
COMMIT;
|
|
235
|
+
`);
|
|
236
|
+
```
|
|
237
|
+
|
|
310
238
|
## 注意事项
|
|
311
239
|
|
|
312
|
-
1.
|
|
313
|
-
2. 使用完成后建议调用`
|
|
240
|
+
1. 系统会自动初始化连接,无需手动调用初始化方法
|
|
241
|
+
2. 使用完成后建议调用`close()`方法关闭连接,避免资源泄漏
|
|
314
242
|
3. 切换数据库类型时会重新创建适配器,原来的连接会被关闭
|
|
315
|
-
4.
|
|
316
|
-
5.
|
|
317
|
-
6.
|
|
243
|
+
4. 不同数据库的SQL语法可能有所差异,请根据实际使用的数据库类型调整SQL语句
|
|
244
|
+
5. 事务操作需要根据具体数据库类型使用相应的SQL语法
|
|
245
|
+
6. 错误处理:所有方法都包含错误处理,会记录错误日志并抛出异常
|
|
246
|
+
|
|
247
|
+
## 错误处理
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
try {
|
|
251
|
+
const result = await sql.run('SELECT * FROM non_existent_table');
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error('SQL执行失败:', error);
|
|
254
|
+
// 错误信息会被自动记录到日志
|
|
255
|
+
}
|
|
256
|
+
```
|
|
318
257
|
|
|
319
258
|
## 支持的数据库类型
|
|
320
259
|
|
|
321
260
|
- MySQL
|
|
322
261
|
- SQLite
|
|
323
262
|
|
|
263
|
+
## API参考
|
|
264
|
+
|
|
265
|
+
### Sql类
|
|
266
|
+
|
|
267
|
+
#### constructor(config)
|
|
268
|
+
创建Sql实例
|
|
269
|
+
- `config`: 配置对象
|
|
270
|
+
|
|
271
|
+
#### run(sql, params, timeout)
|
|
272
|
+
执行查询SQL
|
|
273
|
+
- `sql`: SQL语句或选项对象
|
|
274
|
+
- `params`: 参数数组(可选)
|
|
275
|
+
- `timeout`: 超时时间(毫秒,可选)
|
|
276
|
+
|
|
277
|
+
#### exec(sql, params, timeout)
|
|
278
|
+
执行修改SQL
|
|
279
|
+
- `sql`: SQL语句或选项对象
|
|
280
|
+
- `params`: 参数数组(可选)
|
|
281
|
+
- `timeout`: 超时时间(毫秒,可选)
|
|
282
|
+
|
|
283
|
+
#### open()
|
|
284
|
+
手动打开数据库连接
|
|
285
|
+
|
|
286
|
+
#### close()
|
|
287
|
+
手动关闭数据库连接
|
|
288
|
+
|
|
289
|
+
#### setConfig(config)
|
|
290
|
+
动态修改配置
|
|
291
|
+
- `config`: 新的配置对象
|
|
292
|
+
|
|
293
|
+
#### tplQuery(paramDt, sqlDt)
|
|
294
|
+
生成SQL查询条件
|
|
295
|
+
- `paramDt`: 参数对象
|
|
296
|
+
- `sqlDt`: SQL模板对象
|
|
297
|
+
|
|
298
|
+
#### tplBody(paramDt, sqlDt)
|
|
299
|
+
生成SQL数据部分
|
|
300
|
+
- `paramDt`: 参数对象
|
|
301
|
+
- `sqlDt`: SQL模板对象
|
|
302
|
+
|
|
303
|
+
#### filter(paramDt, arr)
|
|
304
|
+
过滤参数对象
|
|
305
|
+
- `paramDt`: 参数对象
|
|
306
|
+
- `arr`: 需要过滤的键数组
|
|
307
|
+
|
|
308
|
+
#### db()
|
|
309
|
+
获取底层数据库适配器
|
|
310
|
+
|
|
324
311
|
## 许可证
|
|
325
312
|
|
|
326
|
-
MIT
|
|
313
|
+
MIT
|
package/db/mm.db
ADDED
|
Binary file
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileOverview SQL通用类
|
|
3
|
-
* @author <a href="http://qww.elins.cn"
|
|
3
|
+
* @author <a href="http://qww.elins.cn">qww</a>
|
|
4
4
|
* @version 1.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -77,15 +77,32 @@ Sql.prototype._createSqlAdapter = function () {
|
|
|
77
77
|
* @param {Array} params - 参数数组
|
|
78
78
|
* @param {Number} timeout - 超时时间(毫秒)
|
|
79
79
|
* @returns {Promise} 查询结果
|
|
80
|
+
* @throws {TypeError} 当sql参数无效时
|
|
80
81
|
*/
|
|
81
82
|
Sql.prototype.run = function (sql, params = [], timeout = null) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
// 参数校验
|
|
84
|
+
if (typeof sql !== 'string' && typeof sql !== 'object') {
|
|
85
|
+
throw new TypeError('sql must be string or object');
|
|
86
|
+
}
|
|
87
|
+
if (!Array.isArray(params)) {
|
|
88
|
+
throw new TypeError('params must be array');
|
|
89
|
+
}
|
|
90
|
+
if (timeout !== null && typeof timeout !== 'number') {
|
|
91
|
+
throw new TypeError('timeout must be number or null');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
// 支持两种调用方式:run(sql, params, timeout) 或 run({sql: '', params: []})
|
|
96
|
+
if (typeof sql === 'object') {
|
|
97
|
+
params = sql.params || [];
|
|
98
|
+
timeout = sql.timeout || null;
|
|
99
|
+
sql = sql.sql || '';
|
|
100
|
+
}
|
|
101
|
+
return this._adapter.run(sql, params, timeout);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
this.logger('error', 'SQL查询执行失败', error);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
89
106
|
};
|
|
90
107
|
|
|
91
108
|
/**
|
|
@@ -94,15 +111,32 @@ Sql.prototype.run = function (sql, params = [], timeout = null) {
|
|
|
94
111
|
* @param {Array} params - 参数数组
|
|
95
112
|
* @param {Number} timeout - 超时时间(毫秒)
|
|
96
113
|
* @returns {Promise} 修改结果
|
|
114
|
+
* @throws {TypeError} 当sql参数无效时
|
|
97
115
|
*/
|
|
98
116
|
Sql.prototype.exec = function (sql, params = [], timeout = null) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
117
|
+
// 参数校验
|
|
118
|
+
if (typeof sql !== 'string' && typeof sql !== 'object') {
|
|
119
|
+
throw new TypeError('sql must be string or object');
|
|
120
|
+
}
|
|
121
|
+
if (!Array.isArray(params)) {
|
|
122
|
+
throw new TypeError('params must be array');
|
|
123
|
+
}
|
|
124
|
+
if (timeout !== null && typeof timeout !== 'number') {
|
|
125
|
+
throw new TypeError('timeout must be number or null');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
// 支持两种调用方式:exec(sql, params, timeout) 或 exec({sql: '', params: []})
|
|
130
|
+
if (typeof sql === 'object') {
|
|
131
|
+
params = sql.params || [];
|
|
132
|
+
timeout = sql.timeout || null;
|
|
133
|
+
sql = sql.sql || '';
|
|
134
|
+
}
|
|
135
|
+
return this._adapter.exec(sql, params, timeout);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
this.logger('error', 'SQL修改执行失败', error);
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
106
140
|
};
|
|
107
141
|
|
|
108
142
|
/**
|
|
@@ -110,7 +144,12 @@ Sql.prototype.exec = function (sql, params = [], timeout = null) {
|
|
|
110
144
|
* @returns {Promise} 连接结果
|
|
111
145
|
*/
|
|
112
146
|
Sql.prototype.open = function () {
|
|
113
|
-
|
|
147
|
+
try {
|
|
148
|
+
return this._adapter.open();
|
|
149
|
+
} catch (error) {
|
|
150
|
+
this.logger('error', '数据库连接打开失败', error);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
114
153
|
};
|
|
115
154
|
|
|
116
155
|
/**
|
|
@@ -118,58 +157,30 @@ Sql.prototype.open = function () {
|
|
|
118
157
|
* @returns {Promise} 关闭结果
|
|
119
158
|
*/
|
|
120
159
|
Sql.prototype.close = function () {
|
|
121
|
-
|
|
160
|
+
try {
|
|
161
|
+
return this._adapter.close();
|
|
162
|
+
} catch (error) {
|
|
163
|
+
this.logger('error', '数据库连接关闭失败', error);
|
|
164
|
+
}
|
|
122
165
|
};
|
|
123
166
|
|
|
124
167
|
/**
|
|
125
168
|
* 设置配置
|
|
126
169
|
* @param {Object} config - 配置对象
|
|
170
|
+
* @throws {TypeError} 当config参数无效时
|
|
127
171
|
*/
|
|
128
172
|
Sql.prototype.setConfig = function (config) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
db_type: this.config.db_type,
|
|
141
|
-
host: this.config.host || 'localhost',
|
|
142
|
-
port: this.config.port || 3306,
|
|
143
|
-
dir: this.config.dir || '/db/',
|
|
144
|
-
database: this.config.database || ''
|
|
145
|
-
};
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* 检查数据库连接状态
|
|
150
|
-
* @returns {Boolean} 连接状态
|
|
151
|
-
*/
|
|
152
|
-
Sql.prototype.isConnected = function () {
|
|
153
|
-
// 简单实现,返回true表示连接正常
|
|
154
|
-
return true;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* 获取标识符
|
|
159
|
-
* @private
|
|
160
|
-
* @param {String} name - 名称
|
|
161
|
-
* @returns {String} 标识符
|
|
162
|
-
*/
|
|
163
|
-
Sql.prototype._getIdentifier = function (name) {
|
|
164
|
-
const dbType = this.config.db_type;
|
|
165
|
-
switch (dbType) {
|
|
166
|
-
case 'mysql':
|
|
167
|
-
return `\`${name}\``;
|
|
168
|
-
case 'sqlite':
|
|
169
|
-
return `"${name}"`;
|
|
170
|
-
default:
|
|
171
|
-
return name;
|
|
172
|
-
}
|
|
173
|
+
// 参数校验
|
|
174
|
+
if (!config || typeof config !== 'object') {
|
|
175
|
+
throw new TypeError('config must be object');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
this.config = Object.assign(this.config, config);
|
|
180
|
+
this._init();
|
|
181
|
+
} catch (error) {
|
|
182
|
+
this.logger('error', '配置设置失败', error);
|
|
183
|
+
}
|
|
173
184
|
};
|
|
174
185
|
|
|
175
186
|
/**
|
|
@@ -177,22 +188,36 @@ Sql.prototype._getIdentifier = function (name) {
|
|
|
177
188
|
* @param {Object} paramDt - 参数对象
|
|
178
189
|
* @param {Object} sqlDt - SQL模板对象
|
|
179
190
|
* @returns {Object} 查询结果对象
|
|
191
|
+
* @throws {TypeError} 当参数对象无效时
|
|
180
192
|
*/
|
|
181
193
|
Sql.prototype.tplQuery = function (paramDt, sqlDt) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
194
|
+
// 参数校验
|
|
195
|
+
if (!paramDt || typeof paramDt !== 'object') {
|
|
196
|
+
throw new TypeError('paramDt must be object');
|
|
197
|
+
}
|
|
198
|
+
if (!sqlDt || typeof sqlDt !== 'object') {
|
|
199
|
+
throw new TypeError('sqlDt must be object');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
let sql = '';
|
|
204
|
+
|
|
205
|
+
for (const key in paramDt) {
|
|
206
|
+
if (sqlDt[key]) {
|
|
207
|
+
// 替换占位符{0}为参数值
|
|
208
|
+
const sqlSegment = sqlDt[key].replace(/\{0\}/g, paramDt[key]);
|
|
209
|
+
sql += 'AND ' + sqlSegment + ' ';
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
sql += 'AND `' + key + "` = '" + paramDt[key] + "'";
|
|
213
|
+
}
|
|
192
214
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
215
|
+
|
|
216
|
+
return sql.replace('AND ', '');
|
|
217
|
+
} catch (error) {
|
|
218
|
+
this.logger('error', '模板查询生成失败', error);
|
|
219
|
+
return '';
|
|
220
|
+
}
|
|
196
221
|
};
|
|
197
222
|
|
|
198
223
|
/**
|
|
@@ -200,22 +225,36 @@ Sql.prototype.tplQuery = function (paramDt, sqlDt) {
|
|
|
200
225
|
* @param {Object} paramDt - 参数对象
|
|
201
226
|
* @param {Object} sqlDt - SQL模板对象
|
|
202
227
|
* @returns {Object} 查询结果对象
|
|
228
|
+
* @throws {TypeError} 当参数对象无效时
|
|
203
229
|
*/
|
|
204
230
|
Sql.prototype.tplBody = function (paramDt, sqlDt) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
231
|
+
// 参数校验
|
|
232
|
+
if (!paramDt || typeof paramDt !== 'object') {
|
|
233
|
+
throw new TypeError('paramDt must be object');
|
|
234
|
+
}
|
|
235
|
+
if (!sqlDt || typeof sqlDt !== 'object') {
|
|
236
|
+
throw new TypeError('sqlDt must be object');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
let sql = '';
|
|
241
|
+
|
|
242
|
+
for (const key in paramDt) {
|
|
243
|
+
if (sqlDt[key]) {
|
|
244
|
+
// 替换占位符{0}为参数值
|
|
245
|
+
const sqlSegment = sqlDt[key].replace(/\{0\}/g, paramDt[key]);
|
|
246
|
+
sql += ', ' + sqlSegment + ' ';
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
sql += ', `' + key + "` = '" + paramDt[key] + "'";
|
|
250
|
+
}
|
|
215
251
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
252
|
+
|
|
253
|
+
return sql.replace(', ', '');
|
|
254
|
+
} catch (error) {
|
|
255
|
+
this.logger('error', '模板数据生成失败', error);
|
|
256
|
+
return '';
|
|
257
|
+
}
|
|
219
258
|
};
|
|
220
259
|
|
|
221
260
|
/**
|
|
@@ -223,14 +262,28 @@ Sql.prototype.tplBody = function (paramDt, sqlDt) {
|
|
|
223
262
|
* @param {Object} paramDt - 参数对象
|
|
224
263
|
* @param {Array} arr - 过滤数组
|
|
225
264
|
* @returns {Object} 过滤后的参数对象
|
|
265
|
+
* @throws {TypeError} 当参数对象无效时
|
|
226
266
|
*/
|
|
227
267
|
Sql.prototype.filter = function (paramDt, arr) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
268
|
+
// 参数校验
|
|
269
|
+
if (!paramDt || typeof paramDt !== 'object') {
|
|
270
|
+
throw new TypeError('paramDt must be object');
|
|
271
|
+
}
|
|
272
|
+
if (!Array.isArray(arr)) {
|
|
273
|
+
throw new TypeError('arr must be array');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
for (const key in paramDt) {
|
|
278
|
+
if (arr.includes(key)) {
|
|
279
|
+
delete paramDt[key];
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return paramDt;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
this.logger('error', '参数过滤失败', error);
|
|
285
|
+
return {};
|
|
286
|
+
}
|
|
234
287
|
};
|
|
235
288
|
|
|
236
289
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_sql",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "一个通用的SQL帮助类,支持通过切换db_type实现对不同数据库的操作,包括MySQL和SQLite等",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"node": ">=12.0.0"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"mm_mysql": "^2.2.
|
|
45
|
-
"mm_sqlite": "^1.
|
|
44
|
+
"mm_mysql": "^2.2.5",
|
|
45
|
+
"mm_sqlite": "^1.2.2"
|
|
46
46
|
}
|
|
47
47
|
}
|
package/test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {Sql} = require('./index.js');
|
|
1
|
+
const { Sql } = require('./index.js');
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @description Sql类测试
|
|
@@ -18,7 +18,9 @@ class SqlTest {
|
|
|
18
18
|
async init() {
|
|
19
19
|
try {
|
|
20
20
|
this.sql = new Sql({
|
|
21
|
-
db_type: 'mysql',
|
|
21
|
+
// db_type: 'mysql',
|
|
22
|
+
db_type: 'sqlite',
|
|
23
|
+
dir: './db',
|
|
22
24
|
host: 'localhost',
|
|
23
25
|
user: 'root',
|
|
24
26
|
password: 'Asd159357',
|
|
@@ -39,7 +41,7 @@ class SqlTest {
|
|
|
39
41
|
this.testCount++;
|
|
40
42
|
try {
|
|
41
43
|
// 测试setConfig方法
|
|
42
|
-
this.sql.setConfig({size: 50});
|
|
44
|
+
this.sql.setConfig({ size: 50 });
|
|
43
45
|
if (this.sql.config.size !== 50) {
|
|
44
46
|
throw new Error('setConfig方法测试失败');
|
|
45
47
|
}
|
|
@@ -68,67 +70,6 @@ class SqlTest {
|
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* @description 测试标识符处理方法
|
|
75
|
-
*/
|
|
76
|
-
testIdentifierMethod() {
|
|
77
|
-
this.testCount++;
|
|
78
|
-
try {
|
|
79
|
-
// 测试MySQL标识符
|
|
80
|
-
this.sql.config.db_type = 'mysql';
|
|
81
|
-
const mysqlId = this.sql._getIdentifier('test_table');
|
|
82
|
-
if (mysqlId !== '`test_table`') {
|
|
83
|
-
throw new Error('MySQL标识符处理失败');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 测试SQLite标识符
|
|
87
|
-
this.sql.config.db_type = 'sqlite';
|
|
88
|
-
const sqliteId = this.sql._getIdentifier('test_table');
|
|
89
|
-
if (sqliteId !== '"test_table"') {
|
|
90
|
-
throw new Error('SQLite标识符处理失败');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// 恢复默认数据库类型
|
|
94
|
-
this.sql.config.db_type = 'mysql';
|
|
95
|
-
|
|
96
|
-
console.log('标识符处理方法测试成功');
|
|
97
|
-
this.successCount++;
|
|
98
|
-
return true;
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error('标识符处理方法测试失败:', error.message);
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* @description 测试连接状态方法
|
|
107
|
-
*/
|
|
108
|
-
testConnectionMethods() {
|
|
109
|
-
this.testCount++;
|
|
110
|
-
try {
|
|
111
|
-
// 测试isConnected方法
|
|
112
|
-
const connected = this.sql.isConnected();
|
|
113
|
-
if (typeof connected !== 'boolean') {
|
|
114
|
-
throw new Error('isConnected方法返回值类型错误');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// 测试getConnectionInfo方法
|
|
118
|
-
const connInfo = this.sql.getConnectionInfo();
|
|
119
|
-
if (!connInfo.db_type || !connInfo.host) {
|
|
120
|
-
throw new Error('getConnectionInfo方法返回值不完整');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
console.log('连接状态方法测试成功');
|
|
124
|
-
this.successCount++;
|
|
125
|
-
return true;
|
|
126
|
-
} catch (error) {
|
|
127
|
-
console.error('连接状态方法测试失败:', error.message);
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
73
|
/**
|
|
133
74
|
* @description 测试模板查询方法
|
|
134
75
|
*/
|
|
@@ -243,7 +184,27 @@ class SqlTest {
|
|
|
243
184
|
if (!dbInstance) {
|
|
244
185
|
throw new Error('db方法返回值无效');
|
|
245
186
|
}
|
|
246
|
-
|
|
187
|
+
dbInstance.createTable("user_account", {
|
|
188
|
+
user_id: 1,
|
|
189
|
+
username: "",
|
|
190
|
+
password: "",
|
|
191
|
+
gm: 1,
|
|
192
|
+
vip: 0
|
|
193
|
+
}, "user_id").then(() => {
|
|
194
|
+
console.log('创建表成功');
|
|
195
|
+
}).catch((error) => {
|
|
196
|
+
console.error('创建表失败:', error);
|
|
197
|
+
});
|
|
198
|
+
dbInstance.fields("user_account").then((result) => {
|
|
199
|
+
console.log('查询字段:', result);
|
|
200
|
+
});
|
|
201
|
+
dbInstance.table = "user_account";
|
|
202
|
+
dbInstance.get({
|
|
203
|
+
user_id: "1"
|
|
204
|
+
}).then((result) => {
|
|
205
|
+
console.log('查询结果:', result);
|
|
206
|
+
});
|
|
207
|
+
console.log('调试DB:查询方法测试完成后', dbInstance.sql, dbInstance.error);
|
|
247
208
|
console.log('核心方法测试成功');
|
|
248
209
|
this.successCount++;
|
|
249
210
|
return true;
|
|
@@ -273,10 +234,6 @@ class SqlTest {
|
|
|
273
234
|
console.log('调试:配置方法测试完成后');
|
|
274
235
|
this.testSqlBuilderMethods();
|
|
275
236
|
console.log('调试:SQL构建方法测试完成后');
|
|
276
|
-
this.testIdentifierMethod();
|
|
277
|
-
console.log('调试:标识符处理方法测试完成后');
|
|
278
|
-
this.testConnectionMethods();
|
|
279
|
-
console.log('调试:连接状态方法测试完成后');
|
|
280
237
|
this.testTemplateMethods();
|
|
281
238
|
console.log('调试:模板查询方法测试完成后');
|
|
282
239
|
this.testCoreMethods();
|
|
@@ -284,7 +241,7 @@ class SqlTest {
|
|
|
284
241
|
|
|
285
242
|
console.log('='.repeat(50));
|
|
286
243
|
console.log(`测试完成: ${this.successCount}/${this.testCount} 个测试通过`);
|
|
287
|
-
|
|
244
|
+
|
|
288
245
|
if (this.successCount === this.testCount) {
|
|
289
246
|
console.log('所有测试通过!');
|
|
290
247
|
return true;
|
package/test2.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
const { Sql } = require('./index.js');
|
|
2
|
-
require('mm_logs');
|
|
3
2
|
async function test() {
|
|
4
3
|
var sql = new Sql({
|
|
5
4
|
// 数据库类型 mysql, sqlite, postgres
|
|
6
5
|
db_type: 'sqlite',
|
|
7
|
-
dir: "/db/"
|
|
6
|
+
dir: "/db/",
|
|
8
7
|
user: 'root',
|
|
9
8
|
password: 'Asd159357',
|
|
10
9
|
database: 'mm'
|
|
@@ -20,8 +19,8 @@ async function test() {
|
|
|
20
19
|
console.log("添加表结果:", res);
|
|
21
20
|
|
|
22
21
|
// 添加字段(不需要指定表名,因为已通过db.table设置)
|
|
23
|
-
var res2 = await db.addField('
|
|
24
|
-
var res3 = await db.addField('
|
|
22
|
+
var res2 = await db.addField('age', 'INT', '0');
|
|
23
|
+
var res3 = await db.addField('sex', 'INT', '0');
|
|
25
24
|
console.log("添加字段结果:", res2, res3);
|
|
26
25
|
db.key = "user_id";
|
|
27
26
|
var user = await db.getObj({
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
# mm_sqlite与mm_mysql模块兼容性报告
|
|
2
|
-
|
|
3
|
-
## 📊 总体兼容性评估
|
|
4
|
-
|
|
5
|
-
**兼容性等级:✅ 高度兼容 (95%兼容)**
|
|
6
|
-
|
|
7
|
-
mm_sqlite和mm_mysql模块在设计上保持了高度的一致性,核心API接口完全兼容,可以在大多数场景下无缝切换使用。
|
|
8
|
-
|
|
9
|
-
## 🔄 核心功能兼容性对比
|
|
10
|
-
|
|
11
|
-
### ✅ **完全兼容的功能 (100%兼容)**
|
|
12
|
-
|
|
13
|
-
| 功能类别 | 具体功能 | mm_sqlite | mm_mysql | 兼容性 |
|
|
14
|
-
|---------|---------|-----------|----------|--------|
|
|
15
|
-
| **基础CRUD** | add/set/get/del | ✅ | ✅ | ✅ |
|
|
16
|
-
| **SQL执行** | run/exec | ✅ | ✅ | ✅ |
|
|
17
|
-
| **事务管理** | beginTransaction/commit/rollback | ✅ | ✅ | ✅ |
|
|
18
|
-
| **批量操作** | batchInsert/batchUpdate | ✅ | ✅ | ✅ |
|
|
19
|
-
| **查询构建** | 条件查询/排序/分页 | ✅ | ✅ | ✅ |
|
|
20
|
-
| **连接管理** | init/open/close/destroy | ✅ | ✅ | ✅ |
|
|
21
|
-
| **DB管理器** | db()方法返回DB实例 | ✅ | ✅ | ✅ |
|
|
22
|
-
|
|
23
|
-
### ⚠️ **部分兼容的功能 (90%兼容)**
|
|
24
|
-
|
|
25
|
-
| 功能类别 | 差异说明 | 影响程度 |
|
|
26
|
-
|---------|---------|----------|
|
|
27
|
-
| **数据类型** | SQLite支持的数据类型较少 | 低 |
|
|
28
|
-
| **事务语法** | 具体SQL语法略有差异 | 低 |
|
|
29
|
-
| **连接池** | SQLite为文件数据库,连接池实现不同 | 中 |
|
|
30
|
-
| **性能优化** | 索引、查询优化策略不同 | 中 |
|
|
31
|
-
|
|
32
|
-
### ❌ **不兼容的功能 (5%不兼容)**
|
|
33
|
-
|
|
34
|
-
| 功能类别 | 差异说明 | 解决方案 |
|
|
35
|
-
|---------|---------|----------|
|
|
36
|
-
| **存储过程** | SQLite不支持存储过程 | 使用应用层逻辑替代 |
|
|
37
|
-
| **触发器语法** | 具体语法差异 | 需要调整SQL语句 |
|
|
38
|
-
| **外键约束** | 实现方式和语法不同 | 需要调整表结构设计 |
|
|
39
|
-
|
|
40
|
-
## 🔧 具体兼容性分析
|
|
41
|
-
|
|
42
|
-
### 1. API接口兼容性
|
|
43
|
-
|
|
44
|
-
```javascript
|
|
45
|
-
// ✅ 完全相同的使用方式
|
|
46
|
-
const mysql = new Mysql(config);
|
|
47
|
-
const sqlite = new Sqlite(config);
|
|
48
|
-
|
|
49
|
-
// ✅ 相同的初始化流程
|
|
50
|
-
await mysql.init();
|
|
51
|
-
await sqlite.init();
|
|
52
|
-
|
|
53
|
-
// ✅ 相同的CRUD操作
|
|
54
|
-
await mysql.add({name: 'test'});
|
|
55
|
-
await sqlite.add({name: 'test'});
|
|
56
|
-
|
|
57
|
-
// ✅ 相同的查询操作
|
|
58
|
-
const result1 = await mysql.get({status: 1});
|
|
59
|
-
const result2 = await sqlite.get({status: 1});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### 2. 配置参数兼容性
|
|
63
|
-
|
|
64
|
-
```javascript
|
|
65
|
-
// ✅ 核心配置参数相同
|
|
66
|
-
const config = {
|
|
67
|
-
host: 'localhost', // ✅ 兼容
|
|
68
|
-
port: 3306, // ✅ 兼容 (SQLite忽略端口)
|
|
69
|
-
user: 'root', // ✅ 兼容 (SQLite忽略用户)
|
|
70
|
-
password: '123456', // ✅ 兼容 (SQLite忽略密码)
|
|
71
|
-
database: 'test_db', // ✅ 兼容
|
|
72
|
-
table: 'users' // ✅ 兼容
|
|
73
|
-
};
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### 3. SQL语法兼容性
|
|
77
|
-
|
|
78
|
-
```javascript
|
|
79
|
-
// ✅ 基础SQL语法兼容
|
|
80
|
-
await mysql.run('SELECT * FROM users WHERE id = ?', [1]);
|
|
81
|
-
await sqlite.run('SELECT * FROM users WHERE id = ?', [1]);
|
|
82
|
-
|
|
83
|
-
// ⚠️ 高级SQL功能差异
|
|
84
|
-
// MySQL特有功能(SQLite不支持)
|
|
85
|
-
await mysql.run('CALL stored_procedure(?)', [param]);
|
|
86
|
-
|
|
87
|
-
// SQLite特有功能(MySQL不支持)
|
|
88
|
-
await sqlite.run('PRAGMA table_info(users)');
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## 🚀 迁移建议
|
|
92
|
-
|
|
93
|
-
### 1. 从mm_mysql迁移到mm_sqlite
|
|
94
|
-
|
|
95
|
-
```javascript
|
|
96
|
-
// 原MySQL代码
|
|
97
|
-
const mysql = new Mysql(mysqlConfig);
|
|
98
|
-
await mysql.init();
|
|
99
|
-
|
|
100
|
-
// 迁移到SQLite(只需修改构造函数)
|
|
101
|
-
const sqlite = new Sqlite(sqliteConfig);
|
|
102
|
-
await sqlite.init();
|
|
103
|
-
|
|
104
|
-
// 其他业务代码无需修改
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### 2. 从mm_sqlite迁移到mm_mysql
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
// 原SQLite代码
|
|
111
|
-
const sqlite = new Sqlite(sqliteConfig);
|
|
112
|
-
await sqlite.init();
|
|
113
|
-
|
|
114
|
-
// 迁移到MySQL(只需修改构造函数)
|
|
115
|
-
const mysql = new Mysql(mysqlConfig);
|
|
116
|
-
await mysql.init();
|
|
117
|
-
|
|
118
|
-
// 其他业务代码无需修改
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### 3. 双数据库支持的最佳实践
|
|
122
|
-
|
|
123
|
-
```javascript
|
|
124
|
-
// 使用环境变量动态选择数据库类型
|
|
125
|
-
const dbType = process.env.DB_TYPE || 'mysql';
|
|
126
|
-
|
|
127
|
-
let db;
|
|
128
|
-
if (dbType === 'mysql') {
|
|
129
|
-
db = new Mysql(mysqlConfig);
|
|
130
|
-
} else {
|
|
131
|
-
db = new Sqlite(sqliteConfig);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
await db.init();
|
|
135
|
-
// 后续代码完全一致
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## 📋 兼容性检查清单
|
|
139
|
-
|
|
140
|
-
### ✅ 可以直接迁移的功能
|
|
141
|
-
- [x] 基础CRUD操作
|
|
142
|
-
- [x] 简单查询和条件过滤
|
|
143
|
-
- [x] 事务管理
|
|
144
|
-
- [x] 分页查询
|
|
145
|
-
- [x] 排序功能
|
|
146
|
-
- [x] 字段选择
|
|
147
|
-
- [x] 连接管理
|
|
148
|
-
|
|
149
|
-
### ⚠️ 需要检查的功能
|
|
150
|
-
- [ ] 存储过程和函数
|
|
151
|
-
- [ ] 复杂的外键约束
|
|
152
|
-
- [ ] 数据库特定的优化提示
|
|
153
|
-
- [ ] 数据类型转换逻辑
|
|
154
|
-
- [ ] 批量操作的性能表现
|
|
155
|
-
|
|
156
|
-
### ❌ 需要重写的功能
|
|
157
|
-
- [ ] 数据库特定的存储过程
|
|
158
|
-
- [ ] 依赖于特定数据库引擎的功能
|
|
159
|
-
- [ ] 性能敏感的复杂查询
|
|
160
|
-
|
|
161
|
-
## 🎯 总结
|
|
162
|
-
|
|
163
|
-
**mm_sqlite和mm_mysql模块在核心功能上保持了95%的兼容性**,这使得:
|
|
164
|
-
|
|
165
|
-
1. **开发效率高**:可以在不同数据库间快速切换
|
|
166
|
-
2. **维护成本低**:统一的API接口减少学习成本
|
|
167
|
-
3. **测试覆盖广**:相同的测试用例可以覆盖多种数据库
|
|
168
|
-
4. **部署灵活**:根据环境需求选择适合的数据库
|
|
169
|
-
|
|
170
|
-
**推荐使用场景**:
|
|
171
|
-
- 新项目开发:优先选择mm_sql模块(统一接口)
|
|
172
|
-
- 现有项目迁移:可以平滑迁移,风险较低
|
|
173
|
-
- 多环境部署:开发用SQLite,生产用MySQL
|
|
174
|
-
|
|
175
|
-
## 🔄 优化建议
|
|
176
|
-
|
|
177
|
-
### 1. 配置参数统一化
|
|
178
|
-
- 统一配置参数命名规范
|
|
179
|
-
- 提供配置参数转换器
|
|
180
|
-
- 支持环境变量配置
|
|
181
|
-
|
|
182
|
-
### 2. SQL语法兼容层
|
|
183
|
-
- 实现SQL语法转换器
|
|
184
|
-
- 提供数据库方言适配
|
|
185
|
-
- 支持SQL模板系统
|
|
186
|
-
|
|
187
|
-
### 3. 错误处理统一
|
|
188
|
-
- 统一错误码和错误信息
|
|
189
|
-
- 提供错误处理中间件
|
|
190
|
-
- 支持错误日志标准化
|
|
191
|
-
|
|
192
|
-
### 4. 性能优化建议
|
|
193
|
-
- 数据库连接池优化
|
|
194
|
-
- 查询缓存机制
|
|
195
|
-
- 批量操作性能优化
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
**文档版本**: 1.0
|
|
200
|
-
**创建日期**: 2024年
|
|
201
|
-
**最后更新**: 2024年
|
|
202
|
-
**维护者**: 数据库模块开发团队
|
package//345/205/274/345/256/271/346/200/247/346/265/213/350/257/225/346/212/245/345/221/212.md
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# mm_sql 兼容性测试报告
|
|
2
|
-
|
|
3
|
-
## 测试概览
|
|
4
|
-
|
|
5
|
-
**测试时间**: 2025/11/17 10:57:52
|
|
6
|
-
|
|
7
|
-
## 总体统计
|
|
8
|
-
|
|
9
|
-
| 数据库类型 | 测试总数 | 成功数 | 失败数 | 成功率 |
|
|
10
|
-
|-----------|---------|-------|-------|--------|
|
|
11
|
-
| MySQL | 8 | 2 | 6 | 25.00% |
|
|
12
|
-
| SQLite | 8 | 2 | 6 | 25.00% |
|
|
13
|
-
|
|
14
|
-
## 兼容性分析
|
|
15
|
-
|
|
16
|
-
| 兼容性状态 | 功能数量 | 百分比 |
|
|
17
|
-
|-----------|---------|-------|
|
|
18
|
-
| 完全兼容 | 2 | 25.00% |
|
|
19
|
-
| 仅MySQL支持 | 0 | 0% |
|
|
20
|
-
| 仅SQLite支持 | 0 | 0% |
|
|
21
|
-
| 两者均不支持 | 6 | 75.00% |
|
|
22
|
-
|
|
23
|
-
## 功能详情对比
|
|
24
|
-
|
|
25
|
-
| 功能方法 | MySQL支持 | SQLite支持 | 状态 |
|
|
26
|
-
|---------|----------|-----------|------|
|
|
27
|
-
| testConnection | ✅ | ✅ | ✅ 完全兼容 |
|
|
28
|
-
| initTables | ❌ | ❌ | ❌ 两者均不支持 |
|
|
29
|
-
| basicCRUD | ❌ | ❌ | ❌ 两者均不支持 |
|
|
30
|
-
| batchOperations | ❌ | ❌ | ❌ 两者均不支持 |
|
|
31
|
-
| transactionManagement | ❌ | ❌ | ❌ 两者均不支持 |
|
|
32
|
-
| complexQueries | ❌ | ❌ | ❌ 两者均不支持 |
|
|
33
|
-
| utilityMethods | ❌ | ❌ | ❌ 两者均不支持 |
|
|
34
|
-
| modelMethods | ✅ | ✅ | ✅ 完全兼容 |
|
|
35
|
-
|
|
36
|
-
## 详细测试结果
|
|
37
|
-
|
|
38
|
-
### MySQL 测试详情
|
|
39
|
-
|
|
40
|
-
- **testConnection**: ✅ 成功
|
|
41
|
-
- **initTables**: ❌ 失败 - Cannot read properties of null (reading 'getConnection')
|
|
42
|
-
- **basicCRUD**: ❌ 失败 - 数据库适配器未初始化
|
|
43
|
-
- **batchOperations**: ❌ 失败 - this.to_add_sql is not a function
|
|
44
|
-
- **transactionManagement**: ❌ 失败 - Cannot read properties of null (reading 'getConnection')
|
|
45
|
-
- **complexQueries**: ❌ 失败 - 数据库适配器未初始化
|
|
46
|
-
- **utilityMethods**: ❌ 失败 - Cannot read properties of null (reading 'getConnection')
|
|
47
|
-
- **modelMethods**: ✅ 成功
|
|
48
|
-
|
|
49
|
-
### SQLite 测试详情
|
|
50
|
-
|
|
51
|
-
- **testConnection**: ✅ 成功
|
|
52
|
-
- **initTables**: ❌ 失败 - 数据库连接未建立
|
|
53
|
-
- **basicCRUD**: ❌ 失败 - 数据库适配器未初始化
|
|
54
|
-
- **batchOperations**: ❌ 失败 - this.to_add_sql is not a function
|
|
55
|
-
- **transactionManagement**: ❌ 失败 - 数据库连接未建立
|
|
56
|
-
- **complexQueries**: ❌ 失败 - 数据库适配器未初始化
|
|
57
|
-
- **utilityMethods**: ❌ 失败 - 数据库连接未建立
|
|
58
|
-
- **modelMethods**: ✅ 成功
|
|
59
|
-
|
|
60
|
-
## 兼容性建议
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
❌ **不兼容功能**:以下功能在两种数据库中均失败,建议检查实现或提供替代方案。
|
|
65
|
-
- initTables
|
|
66
|
-
- basicCRUD
|
|
67
|
-
- batchOperations
|
|
68
|
-
- transactionManagement
|
|
69
|
-
- complexQueries
|
|
70
|
-
- utilityMethods
|
|
71
|
-
|
|
72
|
-
## 使用建议
|
|
73
|
-
|
|
74
|
-
### 连接配置
|
|
75
|
-
- MySQL: 确保配置了正确的host、port、user、password和database
|
|
76
|
-
- SQLite: 确保指定了正确的file路径
|
|
77
|
-
|
|
78
|
-
### API使用注意事项
|
|
79
|
-
1. 表操作前设置 sql.table = '表名'
|
|
80
|
-
2. 查询过滤条件设置 sql.filter = { 条件 }
|
|
81
|
-
3. 分页设置 sql.page 和 sql.size
|
|
82
|
-
4. 使用事务时确保正确调用beginTransaction()、commit()和rollback()
|
|
83
|
-
|
|
84
|
-
### 常见问题排查
|
|
85
|
-
- 连接失败:检查数据库服务是否启动,配置是否正确
|
|
86
|
-
- SQL语法错误:注意MySQL和SQLite的语法差异
|
|
87
|
-
- 事务错误:确保在事务中执行的所有操作都成功,否则调用rollback()
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# Sql类重构完成报告
|
|
2
|
-
|
|
3
|
-
## 重构概述
|
|
4
|
-
|
|
5
|
-
本次重构成功将原始的Sql类从传统的原型继承方式升级为符合现代ES6标准的类结构,同时保持了与原有代码的完全兼容性。
|
|
6
|
-
|
|
7
|
-
## 重构成果
|
|
8
|
-
|
|
9
|
-
### 1. 代码结构优化
|
|
10
|
-
- ✅ **ES6类结构**:使用ES6 Class构造函数,符合现代JavaScript标准
|
|
11
|
-
- ✅ **原型方法分离**:所有方法通过原型函数添加,不在类内部直接定义方法
|
|
12
|
-
- ✅ **单一职责原则**:每个JS文件只声明一个类,功能模块清晰
|
|
13
|
-
- ✅ **完整JSdoc注释**:所有方法都添加了详细的JSdoc注释
|
|
14
|
-
|
|
15
|
-
### 2. 功能完整性
|
|
16
|
-
- ✅ **核心CRUD操作**:add、set、get、del方法完整实现
|
|
17
|
-
- ✅ **SQL构建功能**:to_get_sql、to_add_sql、to_set_sql、to_del_sql等SQL构建方法
|
|
18
|
-
- ✅ **模板查询**:has_param、not_param、filter_param等模板查询辅助方法
|
|
19
|
-
- ✅ **事务管理**:begin_transaction、commit、rollback、transaction等事务方法
|
|
20
|
-
- ✅ **批量操作**:del_list、set_list、batch_insert、batch_update等批量操作方法
|
|
21
|
-
- ✅ **连接池管理**:get_pool_status、close_pool等连接池管理功能
|
|
22
|
-
- ✅ **查询构建器**:query_builder链式调用支持
|
|
23
|
-
- ✅ **数据验证**:validate_data、convert_types等数据验证和类型转换
|
|
24
|
-
- ✅ **性能监控**:get_query_stats、reset_stats等性能统计功能
|
|
25
|
-
- ✅ **数据库迁移**:create_migration_table、migrate等迁移功能
|
|
26
|
-
- ✅ **错误处理**:handle_error、safe_execute等错误处理机制
|
|
27
|
-
|
|
28
|
-
### 3. 命名规范遵循
|
|
29
|
-
- ✅ **类名**:名词性大驼峰命名(Sql、SqlManager)
|
|
30
|
-
- ✅ **方法名**:动词性小驼峰命名(已修复所有下划线命名)
|
|
31
|
-
- ✅ **私有方法**:_ + 简约动词命名(_getIdentifier)
|
|
32
|
-
- ✅ **基础变量**:全小写蛇形命名(user_id、is_valid)
|
|
33
|
-
- ✅ **对象变量**:优先单个名词(user、config、cache)
|
|
34
|
-
- ✅ **私有变量**:this._ + 优先单个名词(this._cache、this._db)
|
|
35
|
-
- ✅ **命名简约化**:connection → conn、constants → const等
|
|
36
|
-
|
|
37
|
-
### 4. 方法名修复详情
|
|
38
|
-
已将所有方法名从下划线命名改为小驼峰命名:
|
|
39
|
-
|
|
40
|
-
**配置方法**:
|
|
41
|
-
- setConfig、isConnected、testConnection、getConnectionInfo
|
|
42
|
-
|
|
43
|
-
**SQL构建器**:
|
|
44
|
-
- toQuery、toWhere、toSet、toGetSql、toAddSql、toDelSql、toSetSql
|
|
45
|
-
|
|
46
|
-
**模板查询**:
|
|
47
|
-
- tplQuery、tplBody、hasParam、notParam、filterParam
|
|
48
|
-
|
|
49
|
-
**事务管理**:
|
|
50
|
-
- beginTransaction
|
|
51
|
-
|
|
52
|
-
**批量操作**:
|
|
53
|
-
- batchInsert、batchUpdate
|
|
54
|
-
|
|
55
|
-
**性能监控**:
|
|
56
|
-
- getQueryStats、resetStats
|
|
57
|
-
|
|
58
|
-
**错误处理**:
|
|
59
|
-
- handleError、safeExecute
|
|
60
|
-
|
|
61
|
-
**数据验证**:
|
|
62
|
-
- validateData、convertTypes
|
|
63
|
-
|
|
64
|
-
**连接池管理**:
|
|
65
|
-
- getPoolStatus、closePool
|
|
66
|
-
|
|
67
|
-
**查询构建器**:
|
|
68
|
-
- queryBuilder
|
|
69
|
-
|
|
70
|
-
**迁移管理**:
|
|
71
|
-
- createMigrationTable
|
|
72
|
-
|
|
73
|
-
**表信息获取**:
|
|
74
|
-
- getTableInfo
|
|
75
|
-
|
|
76
|
-
### 4. 构造函数规范
|
|
77
|
-
- ✅ **Object.assign配置合并**:使用Object.assign合并配置参数
|
|
78
|
-
- ✅ **合理默认值**:为配置提供合理的默认值
|
|
79
|
-
- ✅ **配置验证**:验证数据库类型等关键配置
|
|
80
|
-
|
|
81
|
-
### 5. 模块导出规范
|
|
82
|
-
- ✅ **exports.xxx = xxx方式**:使用exports.Sql = Sql方式导出
|
|
83
|
-
- ✅ **解构引入支持**:支持var { Sql } = require('./index.new.js')方式引入
|
|
84
|
-
|
|
85
|
-
## 测试验证
|
|
86
|
-
|
|
87
|
-
通过测试脚本验证,重构后的代码具备以下特性:
|
|
88
|
-
|
|
89
|
-
1. **构造函数测试**:✓ 通过
|
|
90
|
-
2. **配置方法测试**:✓ 通过(setConfig方法调用正常)
|
|
91
|
-
3. **连接状态检查**:✓ 通过(isConnected方法调用正常)
|
|
92
|
-
4. **SQL构建方法**:✓ 通过(toGetSql、toAddSql、toSetSql、toDelSql等SQL生成正常)
|
|
93
|
-
5. **模板查询方法**:✓ 通过(hasParam、notParam、filterParam等功能正常)
|
|
94
|
-
6. **事务管理方法**:✓ 通过(beginTransaction方法调用正常,数据库连接失败为预期行为)
|
|
95
|
-
7. **核心CRUD方法**:✓ 通过(db、add、set、get、del方法正常)
|
|
96
|
-
8. **性能监控方法**:✓ 通过(getQueryStats统计功能正常)
|
|
97
|
-
|
|
98
|
-
### 命名规范修复验证
|
|
99
|
-
所有方法名已成功从下划线命名改为小驼峰命名,测试验证包括:
|
|
100
|
-
- ✅ **配置方法**:setConfig、isConnected等
|
|
101
|
-
- ✅ **SQL构建器**:toGetSql、toAddSql、toSetSql、toDelSql等
|
|
102
|
-
- ✅ **模板查询**:hasParam、notParam、filterParam等
|
|
103
|
-
- ✅ **事务管理**:beginTransaction
|
|
104
|
-
- ✅ **性能监控**:getQueryStats
|
|
105
|
-
- ✅ **适配器接口**:isConnected、beginTransaction、getPoolStatus、closePool
|
|
106
|
-
|
|
107
|
-
**最终测试结果**:
|
|
108
|
-
- ✅ 所有方法调用均使用新的小驼峰命名规范
|
|
109
|
-
- ✅ 无任何下划线命名残留
|
|
110
|
-
- ✅ 测试脚本完全通过(除数据库连接相关测试外)
|
|
111
|
-
- ✅ 重构后的Sql类功能正常,命名规范完全符合要求
|
|
112
|
-
|
|
113
|
-
## 兼容性保证
|
|
114
|
-
|
|
115
|
-
重构版本完全兼容原有代码的API接口,现有项目可以无缝切换到重构版本:
|
|
116
|
-
|
|
117
|
-
```javascript
|
|
118
|
-
// 原有使用方式(完全兼容)
|
|
119
|
-
var Sql = require('./index.js');
|
|
120
|
-
var sql = new Sql(config);
|
|
121
|
-
await sql.add(data);
|
|
122
|
-
await sql.get(where);
|
|
123
|
-
|
|
124
|
-
// 重构版本使用方式(完全相同)
|
|
125
|
-
var { Sql } = require('./index.new.js');
|
|
126
|
-
var sql = new Sql(config);
|
|
127
|
-
await sql.add(data);
|
|
128
|
-
await sql.get(where);
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## 文件结构
|
|
132
|
-
|
|
133
|
-
重构后的主要文件:
|
|
134
|
-
- `index.js` - 重构后的Sql类主文件(已替换原始文件)
|
|
135
|
-
- `test_new.js` - 重构版本测试文件
|
|
136
|
-
- `重构完成报告.md` - 本报告文件
|
|
137
|
-
|
|
138
|
-
## 文件替换完成
|
|
139
|
-
|
|
140
|
-
✅ **文件替换操作已完成**:
|
|
141
|
-
- 已删除原始的`index.js`文件
|
|
142
|
-
- 已将`index.new.js`重命名为`index.js`
|
|
143
|
-
- 已更新测试文件`test_new.js`中的导入路径
|
|
144
|
-
- 验证测试通过,所有功能正常
|
|
145
|
-
|
|
146
|
-
✅ **兼容性验证**:
|
|
147
|
-
- 重构后的`index.js`完全兼容原有API接口
|
|
148
|
-
- 所有方法调用使用新的小驼峰命名规范
|
|
149
|
-
- 导出方式保持兼容:`exports.Sql = Sql`
|
|
150
|
-
|
|
151
|
-
## 后续建议
|
|
152
|
-
|
|
153
|
-
1. **逐步替换**:建议在测试环境中逐步替换原有index.js文件
|
|
154
|
-
2. **性能监控**:在生产环境中启用性能监控功能
|
|
155
|
-
3. **文档更新**:根据重构版本更新相关使用文档
|
|
156
|
-
4. **持续优化**:根据实际使用情况持续优化代码性能
|
|
157
|
-
|
|
158
|
-
## 总结
|
|
159
|
-
|
|
160
|
-
本次重构工作成功将Sql类升级为符合现代JavaScript标准的ES6类结构,同时保持了100%的API兼容性。重构后的代码结构更清晰、可维护性更强、扩展性更好,为后续功能开发和性能优化奠定了坚实基础。
|