├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── lib ├── Common │ ├── common.js │ └── function.js ├── Conf │ └── config.js ├── Lib │ ├── Db.js │ ├── MysqlDb.js │ ├── MysqlSocket.js │ └── ParseSql.js └── Mysql.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test/* 3 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-mysql-promise 2 | 3 | 4 | ## Install 5 | $ npm install node-mysql-promise 6 | 7 | ## Introduction 8 | node mysql操作封装类,基于promise,借鉴75team开源项目thinkjs中model操作,数据库连接使用node-mysql的连接池。 9 | ### 使用示例 10 | 11 | ```js 12 | var Mysql = require('node-mysql-promise'); 13 | var mysql = Mysql.createConnection({ 14 | host : 'localhost', 15 | user : 'username', 16 | password : 'password' 17 | }); 18 | //SELECT * FROM table; 19 | mysql.table('table').select().then(function (data) { 20 | console.log(data); 21 | }).catch(function (e) { 22 | console.log(e); 23 | }); 24 | ``` 25 | 26 | 27 | ##API 28 | ### 配置 29 | * `host`: 连接的host(默认: localhost) 30 | * `port`: 连接端口 31 | * `user`: 用户名 32 | * `password`: 密码 33 | * `database`: 数据库名 34 | * `tablePrefix`: 数据表前缀 35 | * `charset`: 编码(默认: UTF8_GENERAL_CI) 36 | * `timezone`: 时区(默认: 'local') 37 | * `connectTimeout`: 连接超时时间(默认: 10000) 38 | * `connectionLimit`: 最大连接数(默认: 10) 39 | * `logSql`: 控制台输出sql(默认: false) 40 | 41 | ### 方法 42 | 43 | ##### table(tableName) 44 | 设置要查询的表(必需) 45 | 46 | * `tableName` String 要查询的表 47 | * `return` this 48 | 49 | ```js 50 | //SELECT * FROM `table` 51 | mysql.table('table').select() 52 | ``` 53 | 54 | 55 | #### field(field, reverse) 56 | 设置要查询的字段 57 | 58 | * `field` String|Array 要查询的字段,可以是字符串,也可以是数组 59 | * `reverse` Boolean 是否反选字段 60 | * `return` this 61 | 62 | ```js 63 | //SELECT * FROM `table` 64 | mysql.table('table').field().select(); 65 | //SELECT `id`, `title` FROM `table` 66 | mysql.table('table').field('id, title').select(); 67 | //SELECT `id`, `title` FROM `table` 68 | mysql.table(['id', 'title']).select(); 69 | //SELECT `author`, `date` FROM `table` 70 | mysql.table('table').field(['id', 'title'], true).select(); 71 | ``` 72 | 73 | 74 | #### limit(offset, length) 75 | 76 | 设置查询的数量 77 | * `offset` Number 起始位置 78 | * `length` Number 查询的数目 79 | * `return` this 80 | 81 | ```js 82 | //SELECT * FROM `table` LIMIT 10 83 | mysql.table('table').limit(10).select(); 84 | //SELECT * FROM `table` LIMIT 10, 20 85 | mysql.table('table').limit(10, 20).select(); 86 | ``` 87 | 88 | #### page(page, listRows) 89 | 设置当前查询的页数,页数从1开始 90 | 91 | * `page` Number 当前的页数 92 | * `listRows` Number 一页记录条数,默认20条 93 | * `return` this 94 | 95 | ```js 96 | //SELECT * FROM `table` 97 | mysql.table('table').page().select(); 98 | //SELECT * FROM `table` LIMIT 0,20 99 | mysql.table('table').page(1).select(); 100 | //SELECT * FROM `table` LIMIT 10, 20 101 | mysql.table('table').page(2, 10).select(); 102 | ``` 103 | 104 | #### union(union, all) 105 | 联合查询 106 | 107 | * `union` String 联合查询的字符串 108 | * `all` 是否为UNION ALL模式 109 | * `return` this 110 | 111 | ```js 112 | //SELECT * FROM `table` UNION (SELECT * FROM `table2`) 113 | mysql.table('table').union('SELECT * FROM `table2`').select(); 114 | //SELECT * FROM `table` UNION ALL (SELECT * FROM `table2`) 115 | mysql.table('table').union('SELECT * FROM `table2`', true).select(); 116 | //SELECT * FROM `table` UNION ALL (SELECT * FROM `table2`) 117 | mysql.table('table').union({table: 'table2'}, true); 118 | //SELECT * FROM `table` UNION ALL (SELECT * FROM `table2`) UNION (SELECT * FROM `table3`) 119 | mysql.table('table').UNION({table: 'table2`}, true).union({table: 'table3'}); 120 | ``` 121 | 122 | 123 | #### join(join) 124 | 组合查询 125 | 126 | * `join` String|Array|Object 127 | * `return` this 128 | 129 | ```js 130 | //SELECT * FROM `table` LEFT JOIN `table2` ON table.id = table2.id 131 | mysql.table('table').join('table2 on table.id = table2.id').select(); 132 | //SELECT * FROM `table` LEFT JOIN `table2` ON table.id = table2.id RIGHT JOIN `table3` ON table.sid = table3.sid 133 | mysql.table('table').join('table2 ON table.id = table2.id', 'RIGHT JOIN table3 ON table.sid = table3.sid').select(); 134 | //SELECT * FROM `table` INNER JOIN `table2` on table.id = table2.id 135 | mysql.table('table').join({ 136 | table: 'table2', 137 | join: 'inner',//left, right, inner三种方式 138 | as: 'c' //表别名 139 | on: ['id', 'id'] //ON 条件 140 | }).select(); 141 | //SELECT * FROM `table` AS a LEFT JOIN `table2` AS b ON a.id = b.id LEFT JOIN `table3` AS c ON a.sid = c.sid 142 | mysql.table('table').alias('a').join({ 143 | table: 'table2', 144 | join: 'left', 145 | as: 'b' 146 | on: ['id', 'id'] 147 | }).join({ 148 | table: 'table3', 149 | join: 'left', 150 | as: 'c', 151 | on: ['sid', 'sid'] 152 | }).select(); 153 | //SELECT * FROM `table` AS a LEFT JOIN `table2` AS b ON a.id = b.id LEFT JOIN `table3` AS c ON a.sid = c.sid 154 | mysql.table('table').join({ 155 | table2: { 156 | join: 'left', 157 | as: 'b', 158 | on: ['id', 'id'] 159 | }, 160 | table3: { 161 | join: 'left', 162 | as: 'c', 163 | on: ['sid', 'sid'] 164 | } 165 | }).select(); 166 | //SELECT * FROM `table` LEFT JOIN `table2` ON table.id = table2.id LEFT JOIN `table3` ON (table.sid = table3.sid AND table.name = table3.title); 167 | mysql.table('table').join({ 168 | table2: { 169 | on: ['id', 'id'] 170 | }, 171 | table3: { 172 | on: { 173 | id: 'id', 174 | title: 'name' 175 | } 176 | } 177 | }).select(); 178 | ``` 179 | 180 | #### order(order) 181 | 设置排序方式 182 | 183 | * `order` String|Array|Obeject 排序方式 184 | * `return` this 185 | 186 | ```js 187 | //SELECT * FROM `table` ORDER BY `id` 188 | mysql.table('table').order('id').select(); 189 | //SELECT * FROM `table` ORDER BY `id` DESC 190 | mysql.table('table').order('id DESC').select(); 191 | //SELECT * FROM `table` ORDER BY `id` DESC, `title` ASC 192 | mysql.table('table').order('id DESC, title ASC').select(); 193 | //SELECT * FROM `table` ORDER BY `id` DESC, `title` ASC 194 | mysql.table('table').order(['id DESC', 'title ASC']).select(); 195 | //SELECT * FROM `table` ORDER BY `id` DESC `title` ASC 196 | mysql.table('table').order({id: 'DESC', title: 'ASC'}).select(); 197 | ``` 198 | 199 | #### alias(alias) 200 | 设置表别名 201 | 202 | * `alias` String 表别名 203 | * `return` this 204 | 205 | ```js 206 | //SELECT * FROM `table` AS t 207 | mysql.table('table').alias('t').select(); 208 | ``` 209 | 210 | #### having(str) 211 | having查询 212 | 213 | * `str` String having查询的字符串 214 | * `return` this 215 | 216 | ```js 217 | //SELECT * FROM `table` HAVING `id` > 1 AND `id` < 100 218 | mysql.table('table').having('id > 1 AND id < 100').select(); 219 | ``` 220 | 221 | #### group(field) 222 | 分组查询 223 | 224 | * `field` String 设定分组查询的字段 225 | * `return` this 226 | 227 | ```js 228 | //SELECT * FROM `table` GROUP BY `date` 229 | mysql.table('table').group('date').select(); 230 | ``` 231 | 232 | #### distinct(field) 233 | 去重查询 234 | 235 | * `field` String 去重的字段 236 | * `return` this 237 | 238 | ```js 239 | //SELECT DISTINCT `title` FROM `table` 240 | mysql.table('table').distinct('title').select(); 241 | ``` 242 | 243 | #### where(where) 244 | 设置where条件 245 | 246 | * `where` Sting|Object 查询条件 247 | * `return` this 248 | 249 | ##### 普通条件 250 | 251 | 252 | ```js 253 | //SELECT * FROM `table` WHERE `id` = 100; 254 | mysql.table('table').where('id = 100').select(); 255 | //SELECT * FROM `table` WHERE `id` = 100; 256 | mysql.table('table').where({id: 100}).select(); 257 | //SELECT * FROM `table` WHERE `id` = 100 OR `id` < 2 258 | mysql.table('table').where('id = 100 OR id < 2').select(); 259 | //SELECT * FROM `table` WHERE `id` != 100 260 | mysql.table('table').where({id: ['!=', 100]}) 261 | ``` 262 | 263 | ##### EXP条件 264 | 默认会对字段和值进行转义,如果不希望被转义,可是使用EXP的方式 265 | 266 | ```js 267 | //SELECT * FROM `table` WHERE `name` = 'name' 268 | mysql.table('table').where({name: ['EXP', "='name'"]}).select(); 269 | //UPDATE `table` SET `num' = `num`+1 270 | mysql.table('table').update({num: ['EXP', 'num+1']}); 271 | ``` 272 | 273 | ##### LIKE条件 274 | 275 | ```js 276 | //SELECT * FROM `table` WHERE (`title` NOT LIKE 'title') 277 | mysql.table('table').where({title: ['NOT LIKE', 'title']}).select(); 278 | //SELECT * FROM `table` WHERE (`title` LIKE '%title%') 279 | mysql.table('table').where({title: ['LIKE', '%title%']}).select(); 280 | //LIKE多个值 281 | //SELECT * FROM `table` WHERE (`title` LIKE 'title' OR `title` LIKE 'name') 282 | mysql.table('table').where({title: ['LIKE', ['title', 'name']]}).select(); 283 | //多个字段LIKE同一个值,OR的关系 284 | //SELECT * FROM `table` WHERE ((`title` LIKE '%title%') OR (`content` LIKE '%title%')) 285 | mysql.table('table').where({'title|content': ['LIKE', '%title%']}).select(); 286 | //多个字段LIKE同一个值,AND的关系 287 | //SELECT * FROM `table` WHERE ((`title` LIKE '%title%') AND (`content` LIKE '%title%')) 288 | mysql.table('table').where({'title&content': ['LIKE', '%title%']}).select(); 289 | ``` 290 | 291 | ##### IN条件 292 | 293 | ```js 294 | //SELECT * FROM `table` WHERE (`id` IN (1,2,3)) 295 | mysql.table('table').where({id: ['IN', '1, 2, 3']}).select(); 296 | //SELECT * FROM `table` WHERE (`id` IN (1, 2, 3)) 297 | mysql.table('table').where({id: ['IN', [1, 2, 3]]}).select(); 298 | //SELECT * FROM `table` WHERE (`id` NOT IN (1, 2, 3)) 299 | mysql.table('table').where({id: ['NOT IN', [1, 2, 3]]}).select(); 300 | ``` 301 | 302 | 303 | ##### 多字段查询 304 | 305 | ```js 306 | //SELECT * FROM `table` WHERE (`id` = 10) AND (`title` = 'title') 307 | mysql.table('table').where({id: 10, title: 'title'}).select(); 308 | //OR 309 | //SELECT * FROM `table` WHERE (`id` = 10) OR (`title` = 'title') 310 | mysql.table('table').where({id: 10, title: 'title', _logic: 'OR'}).select(); 311 | //XOR 312 | //SELECT * FROM `table` WHERE (`id` = 10) XOR (`title` = 'title') 313 | mysql.table('table').where({id: 10, title: 'title', _logic: 'XOR'}).select(); 314 | ``` 315 | 316 | ##### BETWEEN 317 | 318 | ```js 319 | //SELECT * FROM `table` WHERE (`id` BETWEEN 1 AND 2) 320 | mysql.table('table').where({id: ['BETWEEN', 1, 2]}).select(); 321 | //SELECT * FROM `table` WHERE (`id` BETWEEN 1 AND 2) 322 | mysql.table('table').where({id: ['BETWEEN', '1,2']}).select(); 323 | ``` 324 | 325 | ##### 复合查询 326 | 327 | ```js 328 | //SELECT * FROM `table` WHERE `id` > 10 AND `id` < 20 329 | mysql.table('table').where({id: { 330 | '>': 10, 331 | '<': 20 332 | }}).select(); 333 | //SELECT * FROM `table` WHERE `id` < 10 OR `id` > 20 334 | mysql.table('table').where({id: { 335 | '<': 10, 336 | '>': 20, 337 | _logic: 'OR' 338 | }}).select(); 339 | //SELECT * FROM `table` WHERE (`id` > 10 AND `id` < 20) OR (`title` LIKE '%title%') 340 | mysql.table('table').where({id: { 341 | '>': 10, 342 | '<': 20 343 | }, title: ['LIKE', '%title%']}).select(); 344 | //SELECT * FROM `table` WHERE (`title` = 'title') AND ((`id` IN (1, 2, 3)) OR (`content` = 'content')) 345 | mysql.table('table').where({ 346 | title: 'title', 347 | _complex: { 348 | id: ['IN', [1, 2, 3]], 349 | content: 'content', 350 | _logic: 'OR' 351 | } 352 | }).select(); 353 | ``` 354 | 355 | 356 | #### count(field) 357 | 查询符合条件的数目 358 | 359 | * `field` String count的字段 360 | * `return` promise 361 | 362 | ```js 363 | //SELECT COUNT(`id`) FROM `table` LIMIT 1 364 | mysql.table('table').count('id').then(function (count) { 365 | //count为符合条件的数目 366 | }) 367 | ``` 368 | 369 | #### sum(field) 370 | 求和 371 | 372 | * `field` String 要求和的字段 373 | * `return` promise 374 | 375 | ```js 376 | //SELECT SUM(`num`) FROM `table` LIMIT 1 377 | mysql.table('table').sum('num').then(function (sum) { 378 | //sum为求和的值 379 | }); 380 | ``` 381 | 382 | #### max(field) 383 | 求字段的最大值 384 | 385 | * `field` String 要求最大值的字段 386 | * `return` promise 387 | 388 | ```js 389 | //SELECT MAX(`num`) FROM `table` LIMIT 1 390 | mysql.table('table').max('num').then(function (max) { //max为num的最大值 391 | }); 392 | ``` 393 | 394 | #### min(field) 395 | 求字段的最小值 396 | 397 | * `field` String 要求最小值的字段 398 | * `return` promise 399 | 400 | ```js 401 | //SELECT MIN(`num`) FROM `table` LIMIT 1 402 | mysql.table('table').min('num').then(function (min) { 403 | //min为num的最小值 404 | }) 405 | ``` 406 | 407 | 408 | #### avg(field) 409 | 求字段的平均值 410 | 411 | * `field` Sting 要求平均值的字段 412 | * `return` promise 413 | 414 | ```js 415 | //SELECT AVG(`num`) FROM `table` LIMIT 1; 416 | mysql.table('table').avg('num').then(function (avg) { 417 | //avg为num的平均值 418 | }) 419 | ``` 420 | 421 | #### add(data) 422 | 插入数据 423 | 424 | * `data` Object 要插入的数据 425 | * `return` promise 426 | 427 | ```js 428 | var data = { 429 | title: 'title', 430 | content: 'content' 431 | }; 432 | mysql.table('table').add(data).then(function (insertId) { 433 | //如果插入成功,返回插入的id 434 | }).catch(function (err) { 435 | //插入失败,err为具体的错误信息 436 | }) 437 | ``` 438 | 439 | #### thenAdd(data, where, returnDetail) 440 | 当数据表中不存在where条件对应的数据时才进行插入 441 | 442 | * `data` Object 要插入的数据 443 | * `where` String|Array|Object 检测的条件 444 | * `returnDetail` Boolean 是否返回详细的信息 445 | 446 | ```js 447 | //假设字段title为UNIQUE 448 | var data = { 449 | title: 'title', 450 | content: 'content' 451 | }; 452 | var where = { 453 | title: 'title' 454 | } 455 | mysql.table('table').thenAdd(data, where).then(function (id) { 456 | //返回已经存在或者刚插入的id 457 | }) 458 | //返回详细信息 459 | mysql.table('table').thenAdd(data, where, true).then(function (data) { 460 | /* 461 | data数据结构为 462 | { 463 | type: 'exist' || 'add', //exist表示已存在,add新增 464 | id: 1 465 | } 466 | */ 467 | }) 468 | ``` 469 | 470 | #### addAll(data) 471 | 一次添加多条数据 472 | 473 | * `data` Array 474 | * `return` promise 475 | 476 | ```js 477 | var data = [{title: 'xxx'}, {title: 'yyy'}]; 478 | mysql.table('table').addAll(data).then(function (insertId) { 479 | //插入成功 480 | }).catch(function (err) { 481 | //插入失败 482 | }) 483 | ``` 484 | 485 | 486 | #### delete() 487 | 删除数据 488 | 489 | * `return` promise 490 | 491 | ```js 492 | //删除所有数据 493 | mysql.table('table').delete().then(function (affectRows) { 494 | //返回影响行数 495 | }) 496 | //删除符合条件的数据 497 | mysql.table('table').where(where).delete().then(functino (affectRows) { 498 | //返回影响的行数 499 | }) 500 | ``` 501 | 502 | #### update(data) 503 | 更新数据,需要条件 504 | 505 | * `data` Object 要更新的数据 506 | * `return` promise 507 | 508 | ```js 509 | mysql.table('table').where(where).update(data).then(function (affectRows) { 510 | //返回影响行数 511 | }) 512 | ``` 513 | 514 | 515 | #### select() 516 | 查询符合条件的数据 517 | 518 | * `return` promise 519 | 520 | ```js 521 | mysql.table('table').where(where).select().then(function (data) { 522 | //返回结果 Array 523 | }) 524 | ``` 525 | 526 | 527 | #### find() 528 | 查找一条符合条件的数据 529 | 530 | * `return` promise 531 | 532 | ```js 533 | mysql.table('table').where(where).find().then(function (data) { 534 | //返回结果 Object 535 | }) 536 | ``` 537 | 538 | #### updateInc(field, step) 539 | 字段值增加 540 | 541 | * `field` String 要增加的字段 542 | * `step` Number 增加的数值,默认为1 543 | * `return` promise 544 | 545 | ```js 546 | //将id为1的num字段加10 547 | mysql.table('table').where({id: 1}).updateInc('num', 10).then(function () { 548 | }) 549 | ``` 550 | 551 | #### updateDec(field, step) 552 | 字段值减少 553 | 554 | * `field` String 要减少的字段 555 | * `step` Number 减少的数字,默认为1 556 | * `return` promise 557 | 558 | ```js 559 | //将id为1的num字段值减10 560 | mysql.table('table').where({id: 1}).updateDec('num', 10).then(function () { 561 | }) 562 | ``` 563 | 564 | #### getField(field, onlyOne) 565 | 获取某个字段的值 566 | 567 | * `field` String 要获取的字段,可以是多个字段(用,隔开) 568 | * `onlyOne` Boolean|Array 是否只需要一个值,或者是需要几个值 569 | 570 | ```js 571 | //取id>100的id集合 572 | mysql.table('table').where({id: ['>', 100]}).getField('id').then(function (data) { 573 | //data为Array,是符合结果的所有集合 574 | //data = [101, 102, 103, 104] 575 | }) 576 | //只需要id>100的一个值 577 | mysql.table('table').where({id: ['>': 100]}).getField('id', true).then(function (data) { 578 | //data为数字,符合条件的第一个值 579 | //data = 101 580 | }) 581 | //只需要id>100的3个值 582 | mysql.table('table').where({id: ['>' 100]}).getField('id', 3).then(function (data) { 583 | //data为Array 584 | //data = [101, 102, 103] 585 | }) 586 | //需要id和title两个字段的值 587 | mysql.table('table').getField('id, title').then(function (data) { 588 | //data为对象 589 | /* 590 | data = { 591 | id: [101, 102, 103, 104], 592 | title: ['aaaa', 'bbbb', 'cccc', 'dddd'] 593 | } 594 | */ 595 | }) 596 | ``` 597 | 598 | 599 | #### countSelect(options, flag) 600 | 601 | * `options` 查询参数 602 | * `flag` Boolean 当分页值不合法的时候,处理情况。true为修正到第一页,false为修正到最后一页,默认不进行修正 603 | * `return` promise 604 | 605 | ```js 606 | //查询1-20条数据 607 | mysql.table('table').page(1, 20).countSelect().then(function (data) { 608 | //data数据格式 609 | data = { 610 | count: 123, //总条数 611 | total: 7 //总页数 612 | page: 1 //当前页 613 | num: 20 //每页显示数量 614 | data: [{}, {}] //详细数据 615 | } 616 | }); 617 | ``` 618 | 619 | 620 | #### query(sql, parse) 621 | 自定义sql语句进行查询 622 | 623 | * `sql` String 要执行的sql语句 624 | * `parse` 格式参数的数据 625 | * `return` promise 626 | 627 | ```js 628 | var data = [ 629 | '*', 630 | 'table', 631 | 'id > 100' 632 | ] 633 | mysql.query('SELECT %s FROM %s WHERE %s', data).then(function (data) { 634 | }) 635 | ``` 636 | 637 | #### execute(sql, parse) 638 | 自定义sql语句执行,使用与query相同,返回数据不同,execute返回影响行数 639 | 640 | #### close() 641 | 关闭连接池连接,非特殊情况,不建议使用 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * USER: chenlingguang 4 | * TIME: 15/2/9 下午5:51 5 | */ 6 | 7 | var Mysql = require('./lib/Mysql'); 8 | exports.createConnection = function (config) { 9 | return new Mysql(config); 10 | }; -------------------------------------------------------------------------------- /lib/Common/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * USER: chenlingguang 4 | * TIME: 15/2/10 下午2:45 5 | */ 6 | 7 | 'use strict'; 8 | 9 | var Promise = global.Promise; 10 | 11 | /** 12 | * Promise 如果不支持Promise,引用es6-promise 13 | */ 14 | if (!Promise) { 15 | Promise = require('es6-promise').Promise; 16 | } 17 | 18 | exports.Promise = Promise; 19 | 20 | /** 21 | * 继承 22 | * @returns {T} 23 | */ 24 | var extend = exports.extend = function () { 25 | 'use strict'; 26 | var args = [].slice.call(arguments); 27 | var deep = true; 28 | var target = args.shift(); 29 | if (isBoolean(target)) { 30 | deep = target; 31 | target = args.shift(); 32 | } 33 | target = target || {}; 34 | var length = args.length; 35 | var options, name, src, copy, copyAsArray, clone; 36 | for (var i = 0; i < length; i++) { 37 | options = args[i] || {}; 38 | for (name in options) { 39 | src = target[name]; 40 | copy = options[name]; 41 | if (src && src === copy) { 42 | continue; 43 | } 44 | if (deep && copy && (isObject(copy) || (copyAsArray = isArray(copy) ))) { 45 | if (copyAsArray) { 46 | copyAsArray = false; 47 | clone = []; 48 | } else { 49 | clone = src && isObject(src) ? src : {}; 50 | } 51 | target[name] = extend(deep, clone, copy); 52 | } else { 53 | target[name] = copy; 54 | } 55 | } 56 | } 57 | return target; 58 | }; 59 | 60 | /** 61 | * 生成一个promise,如果传入的参数是promise则直接返回 62 | * @param {[type]} obj [description] 63 | * @return {[type]} [description] 64 | */ 65 | var getPromise = exports.getPromise = function (obj, reject) { 66 | 'use strict'; 67 | if (isPromise(obj)) { 68 | return obj; 69 | } 70 | if (reject) { 71 | return Promise.reject(obj); 72 | } 73 | return Promise.resolve(obj); 74 | }; 75 | 76 | /** 77 | * 生成一个defer对象 78 | * @return {[type]} [description] 79 | */ 80 | var getDefer = exports.getDefer = function () { 81 | 'use strict'; 82 | var deferred = {}; 83 | deferred.promise = new Promise(function (resolve, reject) { 84 | deferred.resolve = resolve; 85 | deferred.reject = reject; 86 | }); 87 | return deferred; 88 | }; 89 | 90 | /** 91 | * 生成一个object 92 | * @type {Function} 93 | */ 94 | var getObject = exports.getObject = function(key, value){ 95 | 'use strict'; 96 | var obj = {}; 97 | if (!isArray(key)) { 98 | obj[key] = value; 99 | return obj; 100 | } 101 | key.forEach(function(item, i){ 102 | obj[item] = value[i]; 103 | }); 104 | return obj; 105 | }; 106 | 107 | /** 108 | * 首字母大写 109 | * @type {Function} 110 | */ 111 | var ucfirst = exports.ucfirst = function(name){ 112 | 'use strict'; 113 | name = (name || '') + ''; 114 | return name.substr(0,1).toUpperCase() + name.substr(1).toLowerCase(); 115 | }; 116 | 117 | var toString = Object.prototype.toString; 118 | 119 | /** 120 | * 是否是boolean 121 | * @param obj 122 | * @returns {boolean} 123 | */ 124 | var isBoolean = exports.isBoolean = function (obj) { 125 | 'use strict'; 126 | return toString.call(obj) === '[object Boolean]'; 127 | }; 128 | 129 | /** 130 | * 是否是个数字 131 | * @param obj 132 | * @returns {boolean} 133 | */ 134 | var isNumber = exports.isNumber = function(obj){ 135 | 'use strict'; 136 | return toString.call(obj) === '[object Number]'; 137 | }; 138 | 139 | /** 140 | * 是否是个字符串 141 | * @param obj 142 | * @returns {boolean} 143 | */ 144 | var isString = exports.isString = function(obj){ 145 | 'use strict'; 146 | return toString.call(obj) === '[object String]'; 147 | }; 148 | 149 | /** 150 | * 是否为数字组成的字符串 151 | * @type {RegExp} 152 | */ 153 | var numberReg = /^((\-?\d*\.?\d*(?:e[+-]?\d*(?:\d?\.?|\.?\d?)\d*)?)|(0[0-7]+)|(0x[0-9a-f]+))$/i; 154 | var isNumberString = exports.isNumberString = function(obj){ 155 | 'use strict'; 156 | return numberReg.test(obj); 157 | }; 158 | /** 159 | * 是否是个对象 160 | * @param obj 161 | * @returns {boolean} 162 | */ 163 | var isObject = exports.isObject = function (obj) { 164 | 'use strict'; 165 | if (Buffer.isBuffer(obj)) { 166 | return false; 167 | } 168 | return toString.call(obj) === '[object Object]'; 169 | }; 170 | 171 | /** 172 | * 判断是否是个数组 173 | * @type {Function} 174 | */ 175 | var isArray = exports.isArray = Array.isArray; 176 | 177 | /** 178 | * 是否是个函数 179 | * @param obj 180 | * @returns {boolean} 181 | */ 182 | var isFunction = exports.isFunction = function(obj){ 183 | 'use strict'; 184 | return typeof obj === 'function'; 185 | }; 186 | 187 | /** 188 | * 判断是否是个promise 189 | * @param obj 190 | * @returns {boolean} 191 | */ 192 | var isPromise = exports.isPromise = function (obj) { 193 | 'use strict'; 194 | return !!(obj && typeof obj.then === 'function'); 195 | }; 196 | 197 | /** 198 | * 判断是否是个标量 199 | * @param obj 200 | * @returns {*} 201 | */ 202 | var isScalar = exports.isScalar = function(obj){ 203 | 'use strict'; 204 | return isBoolean(obj) || isNumber(obj) || isString(obj); 205 | }; 206 | 207 | /** 208 | * 判断是否为空 209 | * @type {Function} 210 | */ 211 | var isEmpty = exports.isEmpty = function(obj){ 212 | 'use strict'; 213 | if (isObject(obj)) { 214 | var key; 215 | for(key in obj){ 216 | return false; 217 | } 218 | return true; 219 | }else if (isArray(obj)) { 220 | return obj.length === 0; 221 | }else if (isString(obj)) { 222 | return obj.length === 0; 223 | }else if (isNumber(obj)) { 224 | return obj === 0; 225 | }else if (obj === null || obj === undefined) { 226 | return true; 227 | }else if (isBoolean(obj)) { 228 | return !obj; 229 | } 230 | return false; 231 | }; 232 | -------------------------------------------------------------------------------- /lib/Common/function.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * USER: chenlingguang 4 | * TIME: 15/2/10 下午2:45 5 | */ 6 | -------------------------------------------------------------------------------- /lib/Conf/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * USER: chenlingguang 4 | * TIME: 15/2/10 下午2:45 5 | */ 6 | 7 | exports = module.exports = { 8 | host: 'localhost', 9 | port: 3306, 10 | localAddress: '', 11 | socketPath: '', 12 | user: 'root', 13 | password: '', 14 | database: '', 15 | tablePrefix: '', 16 | charset: 'UTF8_GENERAL_CI', 17 | timezone: 'local', 18 | connectTimeout: 10000, 19 | stringifyObjects: false, 20 | insecureAuth: false, 21 | typeCast: true, 22 | queryFormat: '', 23 | supportBigNumbers: false, 24 | dateStrings: false, 25 | debug: false, 26 | trace: true, 27 | multipleStatements: false, 28 | flags: '', 29 | acquireTimeout: 10000, 30 | waitForConnections: true, 31 | connectionLimit: 10, 32 | queueLimit: 0, 33 | logSql: false, 34 | listRows: 20 35 | }; -------------------------------------------------------------------------------- /lib/Lib/Db.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * USER: chenlingguang 4 | * TIME: 15/2/9 下午5:52 5 | */ 6 | 7 | 8 | module.exports = function () { 9 | return { 10 | //当前SQL 11 | sql: '', 12 | //SQL列表 13 | modelSql: {}, 14 | //配置信息 15 | config: '', 16 | //事务次数 17 | transTimes: 0, 18 | //最后插入id 19 | lastInsertId: 0, 20 | //查询等待 21 | queryWaiting: {}, 22 | //用于查询的sql语句,所有select语句根据该语句解析 23 | selectSql: 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%', 24 | //where条件里的表达式 25 | comparison: { 26 | 'EQ': '=', 27 | 'NEQ': '!=', 28 | '<>': '!=', 29 | 'GT': '>', 30 | 'EGT': '>=', 31 | 'LT': '<', 32 | 'ELT': '<=', 33 | 'NOTLIKE': 'NOT LIKE', 34 | 'LIKE': 'LIKE', 35 | 'IN': 'IN', 36 | 'NOTIN': 'NOT IN' 37 | }, 38 | /** 39 | * 初始化 40 | * @param config 41 | */ 42 | init: function (config) { 43 | this.config = config; 44 | }, 45 | initConnect: function () { 46 | return this.getConnect(); 47 | } 48 | }; 49 | }; -------------------------------------------------------------------------------- /lib/Lib/MysqlDb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * mysql 数据库 3 | * USER: chenlingguang 4 | * TIME: 15/2/9 下午5:52 5 | */ 6 | 7 | var MysqlSocket = require('./MysqlSocket'); 8 | var parseSql = require('./ParseSql'); 9 | var Common = require('../Common/common'); 10 | var extend = Common.extend; 11 | var getDefer = Common.getDefer; 12 | var getPromise = Common.getPromise; 13 | var isString = Common.isString; 14 | var isNumberString = Common.isNumberString; 15 | var isArray = Common.isArray; 16 | var isObject = Common.isObject; 17 | var isBoolean =Common.isBoolean; 18 | var isScalar = Common.isScalar; 19 | 20 | function MysqlDb (config) { 21 | 'use strict'; 22 | this.config = config; 23 | this.init(); 24 | } 25 | 26 | exports = module.exports = MysqlDb; 27 | 28 | /** 29 | * 初始化 30 | */ 31 | MysqlDb.prototype.init = function () { 32 | 'use strict'; 33 | this.mysql = new MysqlSocket(this.config); 34 | }; 35 | 36 | /** 37 | * 获取表字段信息 38 | * @param tableName 39 | * @returns {*|Bluebird.Promise|Promise} 40 | */ 41 | MysqlDb.prototype.getFields = function (tableName) { 42 | 'use strict'; 43 | var self = this; 44 | var sql = "SHOW COLUMNS FROM " + self.parseKey(tableName); 45 | return self.mysql.query(sql).then(function (data) { 46 | var ret = {}; 47 | data.forEach(function (item) { 48 | ret[item.Field] = { 49 | 'name': item.Field, 50 | 'type': item.Type, 51 | 'notnull': item.Null === '', 52 | 'default': item.Default, 53 | 'primary': item.Key === 'PRI', 54 | 'unique': item.Key === 'UNI', 55 | 'autoinc': item.Extra.toLowerCase() === 'auto_increment' 56 | } 57 | }); 58 | return ret; 59 | }) 60 | }; 61 | 62 | /** 63 | * 获取数据库的表信息 64 | * @param dbName 65 | * @returns {*|Bluebird.Promise|Promise} 66 | */ 67 | MysqlDb.prototype.getTables = function (dbName) { 68 | 'use strict'; 69 | var sql = 'SHOW TABLES'; 70 | if (dbName) { 71 | sql += ' FROM ' + dbName; 72 | } 73 | return this.query(sql).then(function(data){ 74 | return data.map(function(item){ 75 | for(var key in item){ 76 | return item[key]; 77 | } 78 | }); 79 | }); 80 | }; 81 | 82 | /** 83 | * 解析key 84 | * @param key 85 | * @returns {string} 86 | */ 87 | MysqlDb.prototype.parseKey = function (key) { 88 | 'use strict'; 89 | key = (key || '').trim(); 90 | if (!(/[,\'\"\*\(\)`.\s]/.test(key))) { 91 | key = '`' + key + '`'; 92 | } 93 | return key; 94 | }; 95 | 96 | /** 97 | * 执行select操作 98 | * @param options 99 | * @returns {*} 100 | */ 101 | MysqlDb.prototype.select = function (options) { 102 | 'use strict'; 103 | var self = this; 104 | var sql; 105 | if (isString(options) && options.toUpperCase().indexOf('SELECT') > -1) { 106 | sql = options; 107 | } else { 108 | options = options || {}; 109 | sql = parseSql.buildSelectSql(options); 110 | } 111 | return self.query(sql); 112 | }; 113 | 114 | /** 115 | * 执行update操作 116 | * @param data 117 | * @param options 118 | * @returns {*|Bluebird.Promise|Promise|*} 119 | */ 120 | MysqlDb.prototype.update = function(data, options) { 121 | 'use strict'; 122 | options = options || {}; 123 | var sql = [ 124 | 'UPDATE ', 125 | parseSql.parseTable(options.table), 126 | parseSql.parseSet(data), 127 | parseSql.parseWhere(options.where), 128 | parseSql.parseOrder(options.order), 129 | parseSql.parseLimit(options.limit), 130 | parseSql.parseLock(options.lock), 131 | parseSql.parseComment(options.comment) 132 | ].join(''); 133 | return this.execute(sql); 134 | }; 135 | 136 | /** 137 | * 插入一条数据 138 | * @param data 139 | * @param options 140 | * @param replace 141 | * @returns {*|Bluebird.Promise|Promise|*} 142 | */ 143 | MysqlDb.prototype.insert = function (data, options, replace) { 144 | 'use strict'; 145 | data = data || {}; 146 | options = options || {}; 147 | var values = []; 148 | var fields = []; 149 | for (var key in data) { 150 | var val = parseSql.parseValue(data[key]); 151 | if (isScalar(key)) { 152 | values.push(val); 153 | fields.push(this.parseKey(key)) 154 | } 155 | } 156 | var sql = (replace ? 'REPLACE' : 'INSERT') + ' INTO '; 157 | sql += parseSql.parseTable(options.table); 158 | sql += ' (' + fields.join(',') + ')'; 159 | sql += ' VALUES (' + values.join(',') + ')'; 160 | sql += parseSql.parseLock(options.lock) + parseSql.parseComment(options.comment); 161 | return this.execute(sql); 162 | }; 163 | 164 | /** 165 | * 插入多条数据 166 | * @param data 167 | * @param options 168 | * @param replace 169 | * @returns {*|Bluebird.Promise|Promise|*} 170 | */ 171 | MysqlDb.prototype.insertAll = function (data, options, replace) { 172 | 'use strict'; 173 | var self = this; 174 | var fields = Object.keys(data[0]); 175 | fields = fields.map(function (item) { 176 | return self.parseKey(item); 177 | }).join(','); 178 | var values = data.map(function (item) { 179 | var value = []; 180 | for (var key in item) { 181 | if (isScalar(key)) { 182 | value.push(parseSql.parseValue(item[key])); 183 | } 184 | } 185 | return '(' + value.join(',') + ')'; 186 | }).join(','); 187 | var sql = (replace ? 'REPLACE' : 'INSERT') + ' INTO '; 188 | sql += parseSql.parseTable(options.table); 189 | sql += ' (' + fields + ') VALUES ' + values; 190 | return self.execute(sql); 191 | }; 192 | 193 | /** 194 | * 执行delete操作 195 | * @param options 196 | * @returns {*|Bluebird.Promise|Promise|*} 197 | */ 198 | MysqlDb.prototype.delete = function (options) { 199 | 'use strict'; 200 | options = options || {}; 201 | var sql = [ 202 | 'DELETE FROM ', 203 | parseSql.parseTable(options.table), 204 | parseSql.parseWhere(options.where), 205 | parseSql.parseOrder(options.order), 206 | parseSql.parseLimit(options.limit), 207 | parseSql.parseLock(options.lock), 208 | parseSql.parseComment(options.comment) 209 | ].join(''); 210 | return this.execute(sql); 211 | }; 212 | 213 | /** 214 | * 执行sql 215 | * @param sql 216 | * @returns {*|type[]} 217 | */ 218 | MysqlDb.prototype.query = function (sql) { 219 | 'use strict'; 220 | var self = this; 221 | return self.mysql.query(sql); 222 | }; 223 | 224 | /** 225 | * 执行sql,返回影响行数 226 | * @param sql 227 | * @returns {*|Bluebird.Promise|Promise} 228 | */ 229 | MysqlDb.prototype.execute = function (sql) { 230 | 'use strict'; 231 | var self = this; 232 | return self.query(sql).then(function (data) { 233 | if (data.insertId) { 234 | return data.insertId; 235 | } 236 | return data.affectedRows || 0; 237 | }); 238 | }; 239 | 240 | 241 | /** 242 | * 关闭连接 243 | */ 244 | MysqlDb.prototype.close = function () { 245 | 'use strict'; 246 | this.mysql.close(); 247 | }; 248 | 249 | /** 250 | * 启动事务 251 | * @returns {*|Bluebird.Promise|Promise|*} 252 | */ 253 | MysqlDb.prototype.startTrans = function () { 254 | 'use strict'; 255 | return this.execute('START TRANSACTION'); 256 | }; 257 | 258 | /** 259 | * 提交事务 260 | * @returns {*|Bluebird.Promise|Promise|*} 261 | */ 262 | MysqlDb.prototype.commit = function () { 263 | 'use strict'; 264 | return this.execute('COMMIT'); 265 | }; 266 | 267 | /** 268 | * 回滚事务 269 | * @returns {*|Bluebird.Promise|Promise|*} 270 | */ 271 | MysqlDb.prototype.rollback = function () { 272 | 'use strict'; 273 | return this.execute('ROLLBACK'); 274 | }; 275 | -------------------------------------------------------------------------------- /lib/Lib/MysqlSocket.js: -------------------------------------------------------------------------------- 1 | /** 2 | * mysql socket 3 | * USER: chenlingguang 4 | * TIME: 15/2/9 下午5:52 5 | */ 6 | 7 | var mysql = require('mysql'); 8 | var extend = require('../Common/common').extend; 9 | var getDefer = require('../Common/common').getDefer; 10 | 11 | function MysqlSocket (config) { 12 | 'use strict'; 13 | this.config = config; 14 | this.init(); 15 | } 16 | 17 | exports = module.exports = MysqlSocket; 18 | 19 | MysqlSocket.prototype.init = function () { 20 | 'use strict'; 21 | var self = this; 22 | //创建连接 23 | self.pool = mysql.createPool(self.config); 24 | }; 25 | 26 | /** 27 | * 执行sql 28 | * @param sql 29 | * @returns {*|type[]} 30 | */ 31 | MysqlSocket.prototype.query = function (sql) { 32 | 'use strict'; 33 | var self = this; 34 | if (self.config.logSql) { 35 | console.log('sql: ' + sql); 36 | } 37 | var deferred = getDefer(); 38 | self.pool.query(sql, function (err, rows) { 39 | if (err) { 40 | deferred.reject(err); 41 | } else { 42 | deferred.resolve(rows); 43 | } 44 | }); 45 | return deferred.promise; 46 | }; 47 | 48 | /** 49 | * 关闭连接 50 | */ 51 | MysqlSocket.prototype.close = function () { 52 | 'use strict'; 53 | this.pool.end() 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /lib/Lib/ParseSql.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * USER: chenlingguang 4 | * TIME: 15/3/4 上午10:10 5 | */ 6 | 7 | 'use strict'; 8 | var Common = require('../Common/common'); 9 | var isString = Common.isString; 10 | var isNumberString = Common.isNumberString; 11 | var isArray = Common.isArray; 12 | var isObject = Common.isObject; 13 | var isBoolean =Common.isBoolean; 14 | var isScalar = Common.isScalar; 15 | var ucfirst = Common.ucfirst; 16 | 17 | module.exports = { 18 | selectSql: 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%', 19 | //where条件里的表达式 20 | comparison: { 21 | 'EQ': '=', 22 | 'NEQ': '!=', 23 | '<>': '!=', 24 | 'GT': '>', 25 | 'EGT': '>=', 26 | 'LT': '<', 27 | 'ELT': '<=', 28 | 'NOTLIKE': 'NOT LIKE', 29 | 'LIKE': 'LIKE', 30 | 'IN': 'IN', 31 | 'NOTIN': 'NOT IN' 32 | }, 33 | parseKey: function (key) { 34 | key = (key || '').trim(); 35 | if (!(/[,\'\"\*\(\)`.\s]/.test(key))) { 36 | key = '`' + key + '`'; 37 | } 38 | return key; 39 | }, 40 | /** 41 | * 解析value 42 | * @param value 43 | */ 44 | parseValue: function (value) { 45 | var self = this; 46 | if (isString(value)) { 47 | value = '\'' + this.escapeString(value) + '\''; 48 | } else if (isArray(value)) { 49 | if ((value[0] + '').toLowerCase() === 'exp') { 50 | value = value[1]; 51 | }else{ 52 | value = value.map(function(item){ 53 | return self.parseValue(item); 54 | }); 55 | } 56 | } else if (isBoolean(value)) { 57 | value = value ? '1' : '0'; 58 | } else if (value === null) { 59 | value = 'null'; 60 | } 61 | return value; 62 | }, 63 | /** 64 | * 解析set集合 65 | * @param data 66 | * @returns {string} 67 | */ 68 | parseSet: function (data) { 69 | var self = this; 70 | data = data || {}; 71 | var set = []; 72 | for (var key in data) { 73 | var value = self.parseValue(data[key]); 74 | if (isScalar(value)) { 75 | set.push(self.parseKey(key) + '=' + value); 76 | } 77 | } 78 | return 'SET ' + set.join(','); 79 | }, 80 | /** 81 | * 解析field 82 | * @param fields 83 | * parseField('name') 84 | * parseField('name, email') 85 | * parseFiled(['name', 'email']) 86 | * parseField({ 87 | * 'xxx_name': 'name', 88 | * 'xxx_email': 'email' 89 | * }) 90 | * @returns {*} 91 | */ 92 | parseField: function (fields) { 93 | var self = this; 94 | if (isString(fields) && fields.indexOf(',') > -1) { 95 | fields = fields.split(','); 96 | } 97 | if (isArray(fields)) { 98 | return fields.map(function (item) { 99 | return self.parseKey(item); 100 | }).join(',') 101 | } else if (isObject(fields)) { 102 | var data = []; 103 | for (var key in fields) { 104 | data.push(self.parseKey(key) + ' AS ' + self.parseKey(fields[key])); 105 | } 106 | return data.join(',') 107 | } else if (isString(fields) && fields) { 108 | return this.parseKey(fields); 109 | } 110 | return '*'; 111 | }, 112 | /** 113 | * 解析table别名 114 | * @param tables 115 | * @returns {string} 116 | */ 117 | parseTable: function (tables) { 118 | var self = this; 119 | if (isString(tables)) { 120 | tables = tables.split(','); 121 | } 122 | if (isArray(tables)) { 123 | return tables.map(function (item) { 124 | return self.parseKey(item); 125 | }).join(','); 126 | } else if (isObject(tables)) { 127 | var data = []; 128 | for (var key in tables) { 129 | data.push(self.parseKey(key) + ' AS ' + self.parseKey(tables[key])); 130 | } 131 | return data.join(',') 132 | } 133 | return ''; 134 | }, 135 | /** 136 | * 解析where条件 137 | * @param where 138 | * @returns {string} 139 | */ 140 | parseWhere: function (where) { 141 | var self = this; 142 | var whereStr = ''; 143 | where = where || {}; 144 | if (isString(where)) { 145 | whereStr = where; 146 | } else { 147 | //定义逻辑运算规则 148 | var oList = ['AND', 'OR', 'XOR']; 149 | var operate = (where._logic + '').toUpperCase(); 150 | delete where._logic; 151 | operate = oList.indexOf(operate) > -1 ? ' ' + operate + ' ' : ' AND '; 152 | 153 | //key值的安全检测正则 154 | var keySafeRegExp = /^[\w\|\&\-\.\(\)\,]+$/; 155 | var multi = where._multi; 156 | delete where._multi; 157 | 158 | var val; 159 | var fn = function (item, i) { 160 | var v = multi ? val[i] : val; 161 | return '(' + self.parseWhereItem(self.parseKey(item), v) + ')'; 162 | }; 163 | for(var key in where){ 164 | key = key.trim(); 165 | val = where[key]; 166 | whereStr += '( '; 167 | if (key.indexOf('_') === 0) { 168 | // 解析特殊条件表达式 169 | whereStr += this.parseSpecialWhere(key, val); 170 | }else{ 171 | if (!keySafeRegExp.test(key)) { 172 | console.log(key + ' is not safe'); 173 | continue; 174 | } 175 | var arr; 176 | // 支持 name|title|nickname 方式定义查询字段 177 | if (key.indexOf('|') > -1) { 178 | arr = key.split('|'); 179 | whereStr += arr.map(fn).join(' OR '); 180 | }else if (key.indexOf('&') > -1) { 181 | arr = key.split('&'); 182 | whereStr += arr.map(fn).join(' AND '); 183 | }else{ 184 | whereStr += this.parseWhereItem(this.parseKey(key), val); 185 | } 186 | } 187 | whereStr += ' )' + operate; 188 | } 189 | whereStr = whereStr.substr(0, whereStr.length - operate.length); 190 | } 191 | return whereStr ? (' WHERE ' + whereStr) : ''; 192 | }, 193 | /** 194 | * 解析单个where条件 195 | * @param key 196 | * @param value 197 | * @returns {string} 198 | */ 199 | parseWhereItem: function (key, val) { 200 | if (isObject(val)) { // {id: {'<': 10, '>': 1}} 201 | var logic = (val._logic || 'AND').toUpperCase(); 202 | delete val._logic; 203 | var result = []; 204 | for(var opr in val){ 205 | var nop = opr.toUpperCase(); 206 | nop = this.comparison[nop] || nop; 207 | result.push(key + ' ' + nop + ' ' + this.parseValue(val[opr])); 208 | } 209 | return result.join(' ' + logic + ' '); 210 | }else if (!isArray(val)) { 211 | return key + ' = ' + this.parseValue(val); 212 | } 213 | var whereStr = ''; 214 | var data; 215 | if (isString(val[0])) { 216 | var val0 = val[0].toUpperCase(); 217 | val0 = this.comparison[val0] || val0; 218 | if (/^(=|!=|>|>=|<|<=)$/.test(val0)) { // 比较运算 219 | whereStr += key + ' ' + val0 + ' ' + this.parseValue(val[1]); 220 | }else if (/^(NOT\s+LIKE|LIKE)$/.test(val0)) { // 模糊查找 221 | if (isArray(val[1])) { //多个like 222 | var likeLogic = (val[2] || 'OR').toUpperCase(); 223 | var likesLogic = ['AND','OR','XOR']; 224 | var self = this; 225 | if (likesLogic.indexOf(likeLogic) > -1) { 226 | var like = val[1].map(function(item){ 227 | return key + ' ' + val0 + ' ' + self.parseValue(item); 228 | }).join(' ' + likeLogic + ' '); 229 | whereStr += '(' + like + ')'; 230 | } 231 | }else{ 232 | whereStr += key + ' ' + val0 + ' ' + this.parseValue(val[1]); 233 | } 234 | }else if(val0 === 'EXP'){ // 使用表达式 235 | whereStr += '(' + key + ' ' + val[1] + ')'; 236 | }else if(val0 === 'IN' || val0 === 'NOT IN'){ // IN 运算 237 | if (val[2] === 'exp') { 238 | whereStr += key + ' ' + val0 + ' ' + val[1]; 239 | }else{ 240 | if (isString(val[1])) { 241 | val[1] = val[1].split(','); 242 | } 243 | //如果不是数组,自动转为数组 244 | if (!isArray(val[1])) { 245 | val[1] = [val[1]]; 246 | } 247 | val[1] = this.parseValue(val[1]); 248 | //如果只有一个值,那么变成=或者!= 249 | if (val[1].length === 1) { 250 | whereStr += key + (val0 === 'IN' ? ' = ' : ' != ') + val[1]; 251 | }else{ 252 | whereStr += key + ' ' + val0 + ' (' + val[1].join(',') + ')'; 253 | } 254 | } 255 | }else if(val0 === 'BETWEEN'){ // BETWEEN运算 256 | data = isString(val[1]) ? val[1].split(',') : val[1]; 257 | if (!isArray(data)) { 258 | data = [val[1], val[2]]; 259 | } 260 | whereStr += ' (' + key + ' ' + val0 + ' ' + this.parseValue(data[0]); 261 | whereStr += ' AND ' + this.parseValue(data[1]) + ')'; 262 | }else{ 263 | console.log('_EXPRESS_ERROR_', key, val); 264 | return ''; 265 | } 266 | }else{ 267 | var length = val.length; 268 | var rule = 'AND'; 269 | if (isString(val[length - 1])) { 270 | var last = val[length - 1].toUpperCase(); 271 | if (last && ['AND', 'OR', 'XOR'].indexOf(last) > -1) { 272 | rule = last; 273 | length--; 274 | } 275 | } 276 | for(var i = 0; i < length; i++){ 277 | var isArr = isArray(val[i]); 278 | data = isArr ? val[i][1] : val[i]; 279 | var exp = ((isArr ? val[i][0] : '') + '').toUpperCase(); 280 | if (exp === 'EXP') { 281 | whereStr += '(' + key + ' ' + data + ') ' + rule + ' '; 282 | }else{ 283 | var op = isArr ? (this.comparison[val[i][0].toUpperCase()] || val[i][0]) : '='; 284 | whereStr += '(' + key + ' ' + op + ' ' + this.parseValue(data) + ') ' + rule + ' '; 285 | } 286 | } 287 | whereStr = whereStr.substr(0, whereStr.length - 4); 288 | } 289 | return whereStr; 290 | }, 291 | /** 292 | * 解析特殊where条件 293 | * @param key 294 | * @param val 295 | * @returns {*} 296 | */ 297 | parseSpecialWhere: function (key, val) { 298 | switch(key){ 299 | // 字符串模式查询条件 300 | case '_string': 301 | return val; 302 | // 复合查询条件 303 | case '_complex': 304 | return this.parseWhere(val).substr(6); 305 | // 字符串模式查询条件 306 | case '_query': 307 | var where = isString(val) ? querystring.parse(val) : val; 308 | var op = ' AND '; 309 | if ('_logic' in where) { 310 | op = ' ' + where._logic.toUpperCase() + ' '; 311 | delete where._logic; 312 | } 313 | var arr = []; 314 | for(var name in where){ 315 | val = where[name]; 316 | val = this.parseKey(name) + ' = ' + this.parseValue(val); 317 | arr.push(val); 318 | } 319 | return arr.join(op); 320 | default: 321 | return ''; 322 | } 323 | return ''; 324 | }, 325 | /** 326 | * 解析limit条件 327 | * @param limit 328 | * @returns {string} 329 | */ 330 | parseLimit: function(limit){ 331 | if (!limit) { 332 | return ''; 333 | } 334 | limit = (limit + '').split(','); 335 | var data = []; 336 | for(var i = 0; i < Math.min(2, limit.length); i++){ 337 | data[i] = limit[i] | 0; 338 | } 339 | return ' LIMIT ' + data.join(','); 340 | }, 341 | /** 342 | * 解析join 343 | * @param join 344 | * @param options 345 | * @returns {string} 346 | */ 347 | parseJoin: function (join, options) { 348 | console.log(options); 349 | if (!join) { 350 | return ''; 351 | } 352 | var joinStr = ''; 353 | var defaultJoin = ' LEFT JOIN '; 354 | if (isArray(join)) { 355 | var joins = { 356 | 'left': ' LEFT JOIN ', 357 | 'right': ' RIGHT JOIN ', 358 | 'inner': ' INNER JOIN ' 359 | }; 360 | join.forEach(function(val){ 361 | if (isString(val)) {//字符串,直接拼接 362 | var hasJoin = val.toLowerCase().indexOf(' join ') > -1; 363 | joinStr += (hasJoin ? ' ' : defaultJoin) + val; 364 | }else if (isObject(val)) { 365 | var ret = []; 366 | if (!('on' in val)) { 367 | for(var key in val){ 368 | var v = val[key]; 369 | v.table = key; 370 | ret.push(v); 371 | } 372 | }else{ 373 | ret.push(val); 374 | } 375 | ret.forEach(function(item){ 376 | var joinType = joins[item.join] || item.join || defaultJoin; 377 | // join 表中包含空格、tab等,认为是sql语句使用情况,与buildSql结合使用 378 | var table = item.table.trim(); 379 | if( /\s+/.test(table) ) { 380 | if( table.indexOf('(') !== 0 ) { 381 | table = '(' + table + ')'; 382 | } 383 | joinStr += joinType + table; 384 | } else { 385 | table = options.tablePrefix + table; 386 | joinStr += joinType + '`' + table + '`'; 387 | } 388 | if (item.as) { 389 | joinStr += ' AS ' + item.as; 390 | } 391 | //ON条件 392 | if (item.on) { 393 | var mTable = options.alias || options.table; 394 | var jTable = item.as || table; 395 | //多个=条件 396 | if (isObject(item.on)) { 397 | var where = []; 398 | for(var key in item.on){ 399 | where.push([ 400 | key.indexOf('.') > -1 ? key : (mTable + '.`' + key + '`'), 401 | '=', 402 | item.on[key].indexOf('.') > -1 ? item.on[key] : (jTable + '.`' + item.on[key] + '`') 403 | ].join('')); 404 | } 405 | joinStr += ' ON (' + where.join(' AND ') + ')'; 406 | }else{ 407 | if (isString(item.on)) { 408 | item.on = item.on.split(/\s*,\s*/); 409 | } 410 | joinStr += ' ON ' + (item.on[0].indexOf('.') > -1 ? item.on[0] : (mTable + '.`' + item.on[0] + '`')); 411 | joinStr += '=' + (item.on[1].indexOf('.') > -1 ? item.on[1] : (jTable + '.`' + item.on[1] + '`')); 412 | } 413 | } 414 | }) 415 | } 416 | }); 417 | }else{ 418 | joinStr += defaultJoin + join; 419 | } 420 | return joinStr; 421 | }, 422 | /** 423 | * 解析order 424 | * @param order 425 | * @returns {string} 426 | */ 427 | parseOrder: function(order){ 428 | var self = this; 429 | if (isArray(order)) { 430 | order = order.map(function(item){ 431 | return self.parseKey(item); 432 | }).join(','); 433 | }else if (isObject(order)) { 434 | var arr = []; 435 | for(var key in order){ 436 | var val = order[key]; 437 | val = this.parseKey(key) + ' ' + val; 438 | arr.push(val); 439 | } 440 | order = arr.join(','); 441 | } 442 | return order ? (' ORDER BY ' + order) : ''; 443 | }, 444 | /** 445 | * 解析group 446 | * @param group 447 | * @returns {string} 448 | */ 449 | parseGroup: function(group){ 450 | if (!group) { 451 | return ''; 452 | } 453 | if (isString(group)) { 454 | group = group.split(','); 455 | } 456 | var result = []; 457 | group.forEach(function(item){ 458 | item = item.trim(); 459 | if (!item) { 460 | return; 461 | } 462 | if (item.indexOf('.') === -1) { 463 | result.push('`' + item + '`'); 464 | }else{ 465 | item = item.split('.'); 466 | result.push(item[0] + '.`' + item[1] + '`'); 467 | } 468 | }) 469 | if (!result.length) { 470 | return ''; 471 | } 472 | return ' GROUP BY ' + result.join(','); 473 | }, 474 | /** 475 | * 解析having 476 | * @param having 477 | * @returns {string} 478 | */ 479 | parseHaving: function(having){ 480 | return having ? (' HAVING ' + having) : ''; 481 | }, 482 | /** 483 | * 解析注释 484 | * @param comment 485 | * @returns {string} 486 | */ 487 | parseComment: function(comment){ 488 | return comment ? (' /* ' + comment + '*/') : ''; 489 | }, 490 | /** 491 | * 解析distinct 492 | * @param distinct 493 | * @returns {string} 494 | */ 495 | parseDistinct: function(distinct){ 496 | return distinct ? ' Distinct ' : ''; 497 | }, 498 | /** 499 | * 解析union 500 | * @param union 501 | * @returns {string} 502 | */ 503 | parseUnion: function(union){ 504 | if (!union) { 505 | return ''; 506 | } 507 | if (isArray(union)) { 508 | var self = this; 509 | var sql = ''; 510 | union.forEach(function(item){ 511 | sql += item.all ? 'UNION ALL ' : 'UNION '; 512 | sql += '(' + (isObject(item.union) ? self.buildSelectSql(item.union).trim() : item.union) + ') '; 513 | }); 514 | return sql; 515 | }else{ 516 | return 'UNION (' + (isObject(union) ? this.buildSelectSql(union).trim() : union) + ') '; 517 | } 518 | }, 519 | /** 520 | * 解析lock 521 | * @param lock 522 | * @returns {string} 523 | */ 524 | parseLock: function(lock){ 525 | if (!lock) { 526 | return ''; 527 | } 528 | return ' FOR UPDATE '; 529 | }, 530 | /** 531 | * page 转成limit 532 | * @param options 533 | * @returns {*|{}} 534 | */ 535 | pageToLimit: function(options){ 536 | options = options || {}; 537 | //根据page生成limit 538 | if ('page' in options) { 539 | var page = options.page + ''; 540 | var listRows = 0; 541 | if (page.indexOf(',') > -1) { 542 | page = page.split(','); 543 | listRows = page[1] | 0; 544 | page = page[0]; 545 | } 546 | page = parseInt(page, 10) || 1; 547 | if (!listRows) { 548 | listRows = isNumberString(options.limit) ? options.limit : this.config.listRows; 549 | } 550 | var offset = listRows * (page - 1); 551 | options.limit = offset + ',' + listRows; 552 | } 553 | return options; 554 | }, 555 | /** 556 | * 拼接select语句 557 | * @param options 558 | * @returns {type[]|promise} 559 | */ 560 | buildSelectSql: function(options){ 561 | options = this.pageToLimit(options); 562 | var sql = this.parseSql(this.selectSql, options); 563 | sql += this.parseLock(options.lock); 564 | return sql; 565 | }, 566 | /** 567 | * 解析sql 568 | * @param sql 569 | * @param options 570 | * @returns {string} 571 | */ 572 | parseSql: function(sql, options){ 573 | options = options || {}; 574 | var self = this; 575 | return sql.replace(/\%([A-Z]+)\%/g, function(a, type){ 576 | type = type.toLowerCase(); 577 | return self['parse' + ucfirst(type)](options[type] || '', options); 578 | }).replace(/__([A-Z_-]+)__/g, function(a, b){ 579 | return '`' + b.toLowerCase() + '`'; 580 | }); 581 | }, 582 | /** 583 | * 转义字符 584 | * @param str 585 | * @returns {*} 586 | */ 587 | escapeString: function(str){ 588 | if (!str) { 589 | return ''; 590 | } 591 | return str.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) { 592 | switch(s) { 593 | case '\0': 594 | return '\\0'; 595 | case '\n': 596 | return '\\n'; 597 | case '\r': 598 | return '\\r'; 599 | case '\b': 600 | return '\\b'; 601 | case '\t': 602 | return '\\t'; 603 | case '\x1a': 604 | return '\\Z'; 605 | default: 606 | return '\\'+s; 607 | } 608 | }); 609 | } 610 | } -------------------------------------------------------------------------------- /lib/Mysql.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * USER: chenlingguang 4 | * TIME: 15/2/9 下午5:51 5 | */ 6 | 7 | var Db = require('./Lib/MysqlDb'); 8 | var defaultConfig = require('./Conf/config'); 9 | var util = require('util'); 10 | var querystring = require('querystring'); 11 | var Common = require('./Common/common'); 12 | var extend = Common.extend; 13 | var getPromise = Common.getPromise; 14 | var getObject = Common.getObject; 15 | var isString = Common.isString; 16 | var isNumber = Common.isNumber; 17 | var isNumberString = Common.isNumberString; 18 | var isArray = Common.isArray; 19 | var isObject = Common.isObject; 20 | var isBoolean = Common.isBoolean; 21 | var isFunction = Common.isFunction; 22 | var isScalar = Common.isScalar; 23 | var isEmpty = Common.isEmpty; 24 | 25 | var Mysql = function (config) { 26 | this.config = extend(defaultConfig, config); 27 | this.tablePrefix = this.config.tablePrefix || ''; 28 | this.tableName = ''; 29 | this.pk = 'id'; 30 | this.fields = {}; 31 | this._data = {}; 32 | this._options = { 33 | tablePrefix: this.tablePrefix 34 | }; 35 | this.init(); 36 | }; 37 | 38 | module.exports = Mysql; 39 | 40 | /** 41 | * 初始化 42 | */ 43 | Mysql.prototype.init = function () { 44 | 'use strict'; 45 | this.db = new Db(this.config); 46 | }; 47 | 48 | /** 49 | * 获取表信息 50 | * @param table 51 | * @param all 52 | * @returns {Promise|*} 53 | */ 54 | Mysql.prototype.getTableFields = function (table, all) { 55 | 'use strict'; 56 | var self = this; 57 | return self.flushFields(table).then(function (fields) { 58 | self.fields = fields; 59 | return getPromise(all ? fields : fields._field); 60 | }) 61 | }; 62 | 63 | /** 64 | * 获取表信息 65 | * @param table 66 | * @returns {Promise|*} 67 | */ 68 | Mysql.prototype.flushFields = function (table) { 69 | 'use strict'; 70 | table = table || this.tableName; 71 | return this.db.getFields(table).then(function (data) { 72 | var fields = { 73 | '_field': Object.keys(data), 74 | '_autoinc': false, 75 | '_unique': [] 76 | }; 77 | var types = {}; 78 | for (var key in data) { 79 | var val = data[key]; 80 | types[key] = val.type; 81 | if (val.primary) { 82 | fields._pk = key; 83 | if (val.autoinc) { 84 | fields._autoinc = true; 85 | } 86 | } else if (val.unique) { 87 | fields._unique.push(key); 88 | } 89 | } 90 | fields._type = types; 91 | return fields; 92 | }); 93 | }; 94 | 95 | /** 96 | * 获取主键 97 | * @returns {*} 98 | */ 99 | Mysql.prototype.getPk = function () { 100 | 'use strict'; 101 | var self = this; 102 | if (isEmpty(self.fields)) { 103 | return this.getTableFields().then(function () { 104 | return self.fields._pk || self.pk; 105 | }) 106 | } 107 | return this.fields._pk || this.pk; 108 | }; 109 | 110 | /** 111 | * 设置表名 112 | * @param table 113 | * @returns {Mysql} 114 | */ 115 | Mysql.prototype.table = function (table) { 116 | 'use strict'; 117 | var tableName = this.tablePrefix + table; 118 | this.tableName = tableName; 119 | this._options.table = tableName; 120 | return this; 121 | }; 122 | 123 | // 链操作方法列表 124 | var methodNameList = [ 125 | 'order', 'alias', 'having', 'group', 126 | 'lock', 'auto', 'filter', 'validate' 127 | ]; 128 | methodNameList.forEach(function (item) { 129 | Mysql.prototype[item] = function (data) { 130 | 'use strict'; 131 | this._options[item] = data; 132 | return this; 133 | }; 134 | }); 135 | 136 | /** 137 | * distinct 138 | * @param data 139 | * @returns {Mysql} 140 | */ 141 | Mysql.prototype.distinct = function (data) { 142 | this._options.distinct = data; 143 | if (isString(data)) { 144 | this._options.field = data; 145 | } 146 | return this; 147 | }; 148 | 149 | /** 150 | * 指定查询数量 151 | * @param offset 152 | * @param length 153 | * @returns {Mysql} 154 | */ 155 | Mysql.prototype.limit = function (offset, length) { 156 | 'use strict'; 157 | if (offset == undefined) { 158 | return this; 159 | } 160 | this._options.limit = length === undefined ? offset : offset + ',' + length; 161 | return this; 162 | }; 163 | 164 | /** 165 | * 分页 166 | * @param page 167 | * @param listRows 168 | * @returns {Mysql} 169 | */ 170 | Mysql.prototype.page = function (page, listRows) { 171 | 'use strict'; 172 | if (page === undefined) { 173 | return this; 174 | } 175 | this._options.page = listRows === undefined ? page : page + ',' + listRows; 176 | return this; 177 | }; 178 | 179 | /** 180 | * where条件 181 | * @param where 182 | * @returns {Mysql} 183 | */ 184 | Mysql.prototype.where = function (where) { 185 | 'use strict'; 186 | if (!where) { 187 | return this; 188 | } 189 | if (isString(where)) { 190 | where = {_string: where}; 191 | } 192 | this._options.where = extend(this._options.where || {}, where); 193 | return this; 194 | }; 195 | 196 | /** 197 | * 设置查询字段 198 | * @param field 199 | * @param reverse 200 | * @returns {Mysql} 201 | */ 202 | Mysql.prototype.field = function (field, reverse) { 203 | 'use strict'; 204 | if (isArray(field)) { 205 | field = field.join(','); 206 | } else if (!field) { 207 | field = '*'; 208 | } 209 | this._options.field = field; 210 | this._options.fieldReverse = reverse; 211 | return this; 212 | }; 213 | 214 | /** 215 | * 联合查询 216 | * @param union 217 | * @param all 218 | * @returns {Mysql} 219 | */ 220 | Mysql.prototype.union = function (union, all) { 221 | 'use strict'; 222 | if (!union) { 223 | return this; 224 | } 225 | if (!this._options.union) { 226 | this._options.union = []; 227 | } 228 | this._options.union.push({ 229 | union: union, 230 | all: all 231 | }); 232 | return this; 233 | }; 234 | 235 | /** 236 | * .join({ 237 | * 'xxx': { 238 | * join: 'left', 239 | * as: 'c', 240 | * on: ['id', 'cid'] 241 | * } 242 | * }) 243 | * 联合查询 244 | * @param join 245 | * @returns {Mysql} 246 | */ 247 | Mysql.prototype.join = function (join) { 248 | 'use strict'; 249 | if (!join) { 250 | return this; 251 | } 252 | if (!this._options.join) { 253 | this._options.join = []; 254 | } 255 | if (isArray(join)) { 256 | this._options.join = this._options.join.concat(join); 257 | } else { 258 | this._options.join.push(join); 259 | } 260 | return this; 261 | }; 262 | 263 | /** 264 | * 解析条件 265 | * @param oriOpts 266 | * @param extraOptions 267 | * @returns {Promise|*} 268 | */ 269 | Mysql.prototype.parseOptions = function (oriOpts, extraOptions) { 270 | 'use strict'; 271 | var self = this; 272 | var options; 273 | if (isScalar(oriOpts)) { 274 | options = extend({}, self._options); 275 | } else { 276 | options = extend({}, self._options, oriOpts, extraOptions); 277 | } 278 | //查询过后清空sql表达式组装 避免影响下次查询 279 | this._options = { 280 | tablePrefix: this.tablePrefix 281 | }; 282 | //获取表名 283 | var table = options.table || self.tableName; 284 | //数据表别名 285 | if (options.alias) { 286 | options.table += ' AS ' + options.alias; 287 | } 288 | var promise = this.getTableFields(table).then(function (fields) { 289 | if (isScalar(oriOpts)) { 290 | options = extend(options, self.parseWhereOptions(oriOpts), extraOptions); 291 | } 292 | return fields; 293 | }); 294 | return promise.then(function (fields) { 295 | // 字段类型验证 296 | if (isObject(options.where) && !isEmpty(fields)) { 297 | var keyReg = /[\.\|\&]/; 298 | // 对数组查询条件进行字段类型检查 299 | for (var key in options.where) { 300 | var val = options.where[key]; 301 | key = key.trim(); 302 | if (fields.indexOf(key) > -1) { 303 | if (isScalar(val) || !val) { 304 | options.where[key] = self.parseType(options.where, key)[key]; 305 | } 306 | } else if (key[0] !== '_' && !keyReg.test(key)) { //字段名不合法,报错 307 | return getPromise(new Error('field `' + key + '` in where condition is not valid'), true); 308 | } 309 | } 310 | } 311 | //field反选 312 | if (options.field && options.fieldReverse) { 313 | //fieldReverse设置为false 314 | options.fieldReverse = false; 315 | var optionsField = options.field.split(','); 316 | options.field = fields.filter(function (item) { 317 | if (optionsField.indexOf(item) > -1) { 318 | return; 319 | } 320 | return item; 321 | }).join(','); 322 | } 323 | return options; 324 | }); 325 | }; 326 | 327 | /** 328 | * 数据类型检测 329 | * @param data 330 | * @param key 331 | * @returns {*} 332 | */ 333 | Mysql.prototype.parseType = function (data, key) { 334 | 'use strict'; 335 | var fieldType = this.fields._type[key] || ''; 336 | if (fieldType.indexOf('bigint') === -1 && fieldType.indexOf('int') > -1) { 337 | data[key] = parseInt(data[key], 10) || 0; 338 | } else if (fieldType.indexOf('double') > -1 || fieldType.indexOf('float') > -1) { 339 | data[key] = parseFloat(data[key]) || 0.0; 340 | } else if (fieldType.indexOf('bool') > -1) { 341 | data[key] = !!data[key]; 342 | } 343 | return data; 344 | }; 345 | 346 | /** 347 | * 对插入到数据库中的数据进行处理,要在parseOptions后执行 348 | * @param data 349 | * @returns {type[]} 350 | */ 351 | Mysql.prototype.parseData = function (data) { 352 | 'use strict'; 353 | //因为会对data进行修改,所以这里需要深度拷贝 354 | data = extend({}, data); 355 | var key; 356 | if (!isEmpty(this.fields)) { 357 | for (key in data) { 358 | var val = data[key]; 359 | if (this.fields._field.indexOf(key) === -1) { 360 | delete data[key]; 361 | } else if (isScalar(val)) { 362 | data = this.parseType(data, key); 363 | } 364 | } 365 | } 366 | //安全过滤 367 | if (isFunction(this._options.filter)) { 368 | for (key in data) { 369 | var ret = this._options.filter.call(this, key, data[key]); 370 | if (ret === undefined) { 371 | delete data[key]; 372 | } else { 373 | data[key] = ret; 374 | } 375 | } 376 | delete this._options.filter; 377 | } 378 | return data; 379 | }; 380 | 381 | /** 382 | * 插入数据 383 | * @param data 384 | * @param options 385 | * @param replace 386 | * @returns {*} 387 | */ 388 | Mysql.prototype.add = function (data, options, replace) { 389 | 'use strict'; 390 | if (options === true) { 391 | replace = true; 392 | options = {}; 393 | } 394 | //copy data 395 | data = extend({}, this._data, data); 396 | this._data = {}; 397 | if (isEmpty(data)) { 398 | return getPromise(new Error('_DATA_TYPE_INVALID_'), true); 399 | } 400 | var self = this; 401 | //解析后的选项 402 | var parsedOptions = {}; 403 | //解析后的数据 404 | var parsedData = {}; 405 | return this.parseOptions(options).then(function (options) { 406 | parsedOptions = options; 407 | return data; 408 | }).then(function (data) { 409 | parsedData = data; 410 | data = self.parseData(data); 411 | return self.db.insert(data, parsedOptions, replace); 412 | }).then(function (insertId) { 413 | return insertId; 414 | }); 415 | }; 416 | 417 | /** 418 | * 当前条件的数据不存在才插入数据 419 | * @param data 420 | * @param where 421 | * @param returnType 422 | * @returns {*|Bluebird.Promise|Promise} 423 | */ 424 | Mysql.prototype.thenAdd = function (data, where, returnType) { 425 | 'use strict'; 426 | if (where === true) { 427 | returnType = true; 428 | where = ''; 429 | } 430 | var self = this; 431 | return this.where(where).find().then(function (findData) { 432 | if (!isEmpty(findData)) { 433 | var idValue = findData[self.getPk()]; 434 | return returnType ? {id: idValue, type: 'exist'} : idValue; 435 | } 436 | return self.table(self.tableName).add(data).then(function (insertId) { 437 | return returnType ? {id: insertId, type: 'add'} : insertId; 438 | }); 439 | }); 440 | }; 441 | 442 | /** 443 | * 插入多条数据 444 | * @param data 445 | * @param options 446 | * @param replace 447 | * @returns {*} 448 | */ 449 | Mysql.prototype.addAll = function (data, options, replace) { 450 | 'use strict'; 451 | if (!isArray(data) || !isObject(data[0])) { 452 | return getPromise(new Error('_DATA_TYPE_INVALID_'), true); 453 | } 454 | if (options === true) { 455 | replace = true; 456 | options = {}; 457 | } 458 | var self = this; 459 | return this.parseOptions(options).then(function (options) { 460 | return self.db.insertAll(data, options, replace); 461 | }).then(function (insertId) { 462 | return insertId; 463 | }); 464 | }; 465 | 466 | /** 467 | * 删除数据 468 | * @param options 469 | * @returns {Promise|*} 470 | */ 471 | Mysql.prototype.delete = function (options) { 472 | 'use strict'; 473 | var self = this; 474 | var parsedOptions = {}; 475 | return this.parseOptions(options).then(function (options) { 476 | parsedOptions = options; 477 | return self.db.delete(options); 478 | }).then(function (affectedRows) { 479 | return affectedRows; 480 | }); 481 | }; 482 | 483 | /** 484 | * 更新数据 485 | * @param data 486 | * @param options 487 | * @returns {*} 488 | */ 489 | Mysql.prototype.update = function (data, options) { 490 | 'use strict'; 491 | var self = this; 492 | data = extend({}, this._data, data); 493 | this._data = {}; 494 | if (isEmpty(data)) { 495 | return getPromise(new Error('_DATA_TYPE_INVALID_'), true); 496 | } 497 | var parsedOptions = {}; 498 | var parsedData = {}; 499 | return this.parseOptions(options).then(function (options) { 500 | parsedOptions = options; 501 | return data; 502 | }).then(function (data) { 503 | var pk = self.getPk(); 504 | parsedData = data; 505 | data = self.parseData(data); 506 | if (isEmpty(parsedOptions.where)) { 507 | // 如果存在主键数据 则自动作为更新条件 508 | if (!isEmpty(data[pk])) { 509 | parsedOptions.where = getObject(pk, data[pk]); 510 | delete data[pk]; 511 | } else { 512 | return getPromise(new Error('_OPERATION_WRONG_'), true); 513 | } 514 | } else { 515 | parsedData[pk] = parsedOptions.where[pk]; 516 | } 517 | return self.db.update(data, parsedOptions); 518 | }).then(function (affectedRows) { 519 | return affectedRows; 520 | }); 521 | }; 522 | 523 | /** 524 | * 更新单个字段的值 525 | * @param field 526 | * @param value 527 | * @returns {*} 528 | */ 529 | Mysql.prototype.updateField = function (field, value) { 530 | 'use strict'; 531 | var data = {}; 532 | if (isObject(field)) { 533 | data = field; 534 | } else { 535 | data[field] = value; 536 | } 537 | return this.update(data); 538 | }; 539 | 540 | /** 541 | * 字段增长 542 | * @param field 543 | * @param step 544 | * @returns {*} 545 | */ 546 | Mysql.prototype.updateInc = function (field, step) { 547 | 'use strict'; 548 | step = parseInt(step, 10) || 1; 549 | return this.updateField(field, ['exp', field + '+' + step]); 550 | }; 551 | 552 | /** 553 | * 字段减少 554 | * @param field 555 | * @param step 556 | * @returns {*} 557 | */ 558 | Mysql.prototype.updateDec = function (field, step) { 559 | 'use strict'; 560 | step = parseInt(step, 10) || 1; 561 | return this.updateField(field, ['exp', field + '-' + step]); 562 | }; 563 | 564 | /** 565 | * 解析options中简洁的where条件 566 | * @param options 567 | * @returns {*|{}} 568 | */ 569 | Mysql.prototype.parseWhereOptions = function (options) { 570 | 'use strict'; 571 | if (isNumber(options) || isString(options)) { 572 | var pk = this.getPk(); 573 | options += ''; 574 | var where = {}; 575 | if (options.indexOf(',') > -1) { 576 | where[pk] = ['IN', options]; 577 | } else { 578 | where[pk] = options; 579 | } 580 | options = { 581 | where: where 582 | }; 583 | } 584 | return options || {}; 585 | }; 586 | 587 | /** 588 | * 查询一条数据 589 | * @param options 590 | * @returns {Promise|*} 591 | */ 592 | Mysql.prototype.find = function (options) { 593 | 'use strict'; 594 | var self = this; 595 | var parsedOptions = {}; 596 | return this.parseOptions(options, {limit: 1}).then(function (options) { 597 | parsedOptions = options; 598 | return self.db.select(options); 599 | }).then(function (data) { 600 | return data[0] || {}; 601 | }); 602 | }; 603 | 604 | /** 605 | * 查询数据 606 | * @param options 607 | * @returns {Promise|*} 608 | */ 609 | Mysql.prototype.select = function (options) { 610 | 'use strict'; 611 | var self = this; 612 | var parsedOptions = {}; 613 | return this.parseOptions(options).then(function (options) { 614 | parsedOptions = options; 615 | return self.db.select(options); 616 | }).then(function (result) { 617 | return result; 618 | }); 619 | }; 620 | 621 | /** 622 | * 返回数据里含有count信息的查询 623 | * @param options 624 | * @param pageFlag 当页面不合法时的处理方式,true为获取第一页,false为获取最后一页,undefined获取为空 625 | * @returns {Promise|*} 626 | */ 627 | Mysql.prototype.countSelect = function (options, pageFlag) { 628 | 'use strict'; 629 | if (isBoolean(options)) { 630 | pageFlag = options; 631 | options = {}; 632 | } 633 | var self = this; 634 | //解析后的options 635 | var parsedOptions = {}; 636 | var result = {}; 637 | return this.parseOptions(options).then(function (options) { 638 | parsedOptions = options; 639 | return self.options({ 640 | where: options.where, 641 | cache: options.cache, 642 | join: options.join, 643 | alias: options.alias, 644 | table: options.table 645 | }).count((options.alias || self.tableName) + '.' + self.getPk()); 646 | }).then(function (count) { 647 | var pageOptions = self.parsePage(parsedOptions); 648 | var totalPage = Math.ceil(count / pageOptions.num); 649 | if (isBoolean(pageFlag)) { 650 | if (pageOptions.page > totalPage) { 651 | pageOptions.page = pageFlag === true ? 1 : totalPage; 652 | } 653 | parsedOptions.page = pageOptions.page + ',' + pageOptions.num; 654 | } 655 | result = extend({count: count, total: totalPage}, pageOptions); 656 | if (!parsedOptions.page) { 657 | parsedOptions.page = pageOptions.page; 658 | } 659 | return self.select(parsedOptions); 660 | }).then(function (data) { 661 | result.data = data; 662 | return result; 663 | }); 664 | }; 665 | 666 | /** 667 | * 668 | * @param field 669 | * @param one 670 | * @returns {*} 671 | */ 672 | 673 | Mysql.prototype.getField = function (field, one) { 674 | 'use strict'; 675 | var self = this; 676 | return this.parseOptions({'field': field}).then(function (options) { 677 | if (isNumber(one)) { 678 | options.limit = one; 679 | } else if (one === true) { 680 | options.limit = 1; 681 | } 682 | return self.db.select(options); 683 | }).then(function (data) { 684 | var multi = field.indexOf(',') > -1; 685 | if (multi) { 686 | var fields = field.split(/\s*,\s*/); 687 | var result = {}; 688 | fields.forEach(function (item) { 689 | result[item] = []; 690 | }); 691 | data.every(function (item) { 692 | fields.forEach(function (fItem) { 693 | if (one === true) { 694 | result[fItem] = item[fItem]; 695 | } else { 696 | result[fItem].push(item[fItem]); 697 | } 698 | }); 699 | return one !== true; 700 | }); 701 | return result; 702 | } else { 703 | data = data.map(function (item) { 704 | return Object.values(item)[0]; 705 | }); 706 | return one === true ? data[0] : data; 707 | } 708 | }); 709 | }; 710 | 711 | /** 712 | * SQL查询 713 | * @param sql 714 | * @param parse 715 | * @returns {Promise|*} 716 | */ 717 | Mysql.prototype.query = function (sql, parse) { 718 | 'use strict'; 719 | var self = this; 720 | if (parse !== undefined && !isBoolean(parse) && !isArray(parse)) { 721 | parse = [].slice.call(arguments, 1); 722 | } 723 | sql = self.parseSql(sql, parse); 724 | return self.db.select(sql).then(function (data) { 725 | self._options = {}; 726 | return data; 727 | }); 728 | }; 729 | 730 | /** 731 | * 执行SQL语句,返回影响行数或者插入id 732 | * @param sql 733 | * @param parse 734 | * @returns {*|*|Bluebird.Promise|Promise|type[]} 735 | */ 736 | Mysql.prototype.execute = function(sql, parse){ 737 | 'use strict'; 738 | if (parse !== undefined && !isBoolean(parse) && !isArray(parse)) { 739 | parse = [].slice.call(arguments, 1); 740 | } 741 | sql = this.parseSql(sql, parse); 742 | return this.db.execute(sql); 743 | }; 744 | 745 | /** 746 | * 解析sql语句 747 | * @type {void|*} 748 | */ 749 | Mysql.prototype.parseSql = function (sql, parse) { 750 | 'use strict'; 751 | if (parse === undefined) { 752 | parse = []; 753 | } else if (!isArray(parse)) { 754 | parse = [parse]; 755 | } 756 | parse.unshift(sql); 757 | sql = util.format.apply(null, parse); 758 | var map = { 759 | '__TABLE__': '`' + this.tableName + '`' 760 | }; 761 | var self = this; 762 | sql = sql.replace(/__([A-Z]+)__/g, function (a, b) { 763 | return map[a] || ('`' + b.toLowerCase() + '`'); 764 | }); 765 | return sql; 766 | }; 767 | 768 | ['count', 'sum', 'min', 'max', 'avg'].forEach(function (item) { 769 | Mysql.prototype[item] = function (field) { 770 | 'use strict'; 771 | field = field || this.pk; 772 | return this.getField(item.toUpperCase() + '(' + field + ') AS `' + item + '`', true); 773 | } 774 | }); 775 | 776 | /** 777 | * 设置操作选项 778 | * @param options 779 | * @returns {*} 780 | */ 781 | Mysql.prototype.options = function (options) { 782 | 'use strict'; 783 | if (options === true) { 784 | return this._options; 785 | } 786 | this._options = options; 787 | return this; 788 | }; 789 | 790 | /** 791 | * 设置数据对象值 792 | * @param data 793 | * @returns {*} 794 | */ 795 | Mysql.prototype.data = function (data) { 796 | 'use strict'; 797 | if (data === true) { 798 | return this._data; 799 | } 800 | if (isString(data)) { 801 | data = querystring.parse(data); 802 | } 803 | this._data = data; 804 | return this; 805 | }; 806 | 807 | /** 808 | * 启动事务 809 | * @returns {Promise|*} 810 | */ 811 | Mysql.prototype.startTrans = function () { 812 | 'use strict'; 813 | var self = this; 814 | return self.db.commit().then(function () { 815 | return self.db.startTrans(); 816 | }) 817 | }; 818 | 819 | /** 820 | * 提交事务 821 | * @returns {*|Bluebird.Promise|Promise|*} 822 | */ 823 | Mysql.prototype.commit = function () { 824 | 'use strict'; 825 | return this.db.commit(); 826 | }; 827 | 828 | /** 829 | * 回滚事务 830 | * @returns {*|Bluebird.Promise|Promise|*} 831 | */ 832 | Mysql.prototype.rollback = function () { 833 | 'use strict'; 834 | return this.db.rollback(); 835 | }; 836 | 837 | /** 838 | * 关闭连接 839 | * @returns {*} 840 | */ 841 | Mysql.prototype.close = function () { 842 | 'use strict'; 843 | return this.db.close(); 844 | }; 845 | 846 | /** 847 | * 解析page 848 | * @param options 849 | * @returns {*} 850 | */ 851 | Mysql.prototype.parsePage = function (options) { 852 | 'use strict'; 853 | if ('page' in options) { 854 | var page = options.page + ''; 855 | var num = 0; 856 | if (page.indexOf(',') > -1) { 857 | page = page.split(','); 858 | num = parseInt(page[1], 10); 859 | page = page[0]; 860 | } 861 | num = num || this.config.listRows; 862 | page = parseInt(page, 10) || 1; 863 | return { 864 | page: page, 865 | num: num 866 | }; 867 | } 868 | return { 869 | page: 1, 870 | num: this.config.listRows 871 | }; 872 | }; 873 | 874 | Object.values = function (obj) { 875 | 'use strict'; 876 | var values = []; 877 | for (var key in obj) { 878 | if (obj.hasOwnProperty(key)) { 879 | values.push(obj[key]) 880 | } 881 | } 882 | return values; 883 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-mysql-promise", 3 | "version": "0.0.6", 4 | "description": "node mysql model based on promise", 5 | "main": "index.js", 6 | "scripts": { 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/ffttpp/node-mysql-promise.git" 11 | }, 12 | "keywords": [ 13 | "node-mysql", 14 | "promise", 15 | "mysql-pool", 16 | "parse-sql" 17 | ], 18 | "author": "ffttpp", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/ffttpp/node-mysql-promise/issues" 22 | }, 23 | "homepage": "https://github.com/ffttpp/node-mysql-promise", 24 | "dependencies": { 25 | "es6-promise": "^2.0.1", 26 | "mysql": "^2.5.4" 27 | } 28 | } 29 | --------------------------------------------------------------------------------