背景
作為前端工程師的我們,可能不懂PHP、Java等語言,但也想創建自己的服務,那么Node.js是一個非常好的選擇,
而在學習或者使用Node.js的時候,不免會與MySQL等資料庫進行連接互動,而筆者在學習Node之前,使用過ThinkPHP5,其中內置的DB類讓我半吊子“后端”用起來十分舒服,其中的鏈式呼叫更是方便無比,
那么此文,我們也嘗試著去封裝一個DB類,畢竟手寫SQL太容易出現錯誤辣,
明確需求
這個環節有點像PM跟我們闡述一個功能需求,假設已經存在這么一個類,我們希望這個類是如何為我所用的?以下為我自己的幾個需求,后面的編碼就從這幾個需求出發,一步步完善我們的類即可,
鏈式呼叫
例如TP5中,如果要查詢user表中id為1的用戶,我們常常這樣子寫:
DB::table('user')->where('id',1)->find();
一開始得確定傳入的表名,也算是我們約定的一個規范吧,而中間環節可以有多個where,類自動幫我們拼接好查詢條件,在最后呼叫的應該是以下幾個常用的方法:
- find 查詢一條記錄
- select 查詢符合條件的記錄
- count 回傳記錄的條數
- update 更新操作
- delete 洗掉操作
- insert 插入操作
例如如果我們的類寫好了,描述上述語法,我希望這樣使用:
DB.table('user').where('id',1).find();
基于Promise封裝
我們知道Node里面查詢資料庫是一個異步操作,而我在使用這個類的時候,希望越簡潔越好,不要讓我去思考一些異步的流程,比如可以像下面這樣:
const result = await DB.table('user').select();
這種使用async/await的寫法我個人是比較喜歡的,看起來代碼邏輯較為清晰一些,
其他的小偏好
find/select方法我希望可以按需取欄位,insert/update的時候我希望傳入一個Object來描述,where傳參的時候如果不穿運算子,默認就是等號,還能支持原生SQL查詢…
常用方法編碼
那大概捋了一下思路之后,我們就開始進行編碼吧,先搭起來一個架子:
class Orm {
constructor(debug = 0) {
this.sql = ''
this._table = ''
this.filter = ''
this.debug = debug
}
count() {}
find() {}
select() {}
insert() {}
delete() {}
update() {}
where(){}
table(){}
}
module.exports = {
Orm
}
上述除了待會要實作的具體方法之外,還有三個我們這個類所用到的變數,
sql最后拼接成的SQL陳述句_table一開始暫存的表名filter過濾條件字串debugdebug模式,如果為1則會打出完整SQL陳述句
而在使用這個類的時候盡量是單例模式,這樣與JS單執行緒的特點結合起來,就不會出現什么沖突,
select 方法實作
在真正開始動手實作我們的第一個查詢方法時,還是回到我們之前說的,如果這個類寫好了,我們希望怎樣去使用它,我希望像下述一樣去使用:
const Orm = require('./orm/index').Orm
const orm = new Orm()
async function select() {
const result = await orm.table('good').select()
console.log('result',result)
}
基于上述最簡單版本的select方法,我們可以開始以下的編碼,
首先table方法的實作就是快取一下表名即可,而這些需要鏈式呼叫的方法,只需在最后加上一句return this即可,
table(table){
this._table = table
return this
}
select的具體實作如下,其實就是拼接一下字串,傳列名則按需取欄位,不傳就取所有欄位,
select(..._cols) {
let cols = [..._cols]
if (cols.length != 0) {
let c = ''
for (let i = 0; i < cols.length; i++) {
let char = cols[i]
if (i != cols.length - 1) {
c += `${char},`
} else {
c += `${char}`
}
}
this.sql = `SELECT ${c} FROM ${this._table} ${this.filter}`
} else {
this.sql = `SELECT * FROM ${this._table} ${this.filter}`
}
//run 方法則為真正去跟資料庫互動的方法
return this.run()
}
find方法與select方法的區別只是find方法在最后加上了LIMIT 1而已,其他具體實作基本一樣
實作中,最后的CURD方法都會調到這個run方法,讓我們來看一下這個run方法的實作,真正與mysql互動前,還需安裝好mysql驅動,
//dbConfig.js
module.exports = {
mysql: {
host: 'localhost',
user: 'root',
password: 'your mysql psw',
database: 'shop',
port: 3306
}
};
const mysql = require('mysql')
const dbConfig = require('./dbConfig')
//創建一個mysql連接池
const pool = mysql.createPool(dbConfig.mysql)
run() {
let _sql = this.sql
//debug模式列印出SQL陳述句
if(this.debug) console.log(_sql)
let table = this._table
//注意在進行資料庫查詢的前一刻,應該把之前快取的字串清空掉,
this.done()
//基于Promise封裝
return new Promise((resolve, reject) => {
if (!table) {
reject('表名不可為空')
} else {
//進行資料庫查詢
pool.getConnection((err, con) => {
if (err) {
reject(err)
} else {
con.query(_sql, (err, result) => {
if (err) {
reject(err)
} else {
resolve(result)
con.release()
}
})
}
})
}
})
}
done(){
this.sql = ''
this._table = ''
this.filter = ''
}
最后再來總結一下流程:
- 暫存表名
- 拼裝SQL
return this保證鏈式呼叫Promise+async/await保證更友好的使用形式
了解了大概的套路之后,之后的編碼就會更加順利了,我們再來實作多幾個方法,
where實作
在使用where的時候,不自覺的就往使用過的TP框架上面靠了,有一個小偏好就是,當傳參只有兩個時,默認的運算子是’=’,
例如
DB.table('user').where('id',1).select()
//等同于
DB.table('user').where('id','=','1').select()
where(...arg) {
let arr = [...arg]
let column = arr[0] || null,
op = arr[1] || null,
val = arr[2] || null
if (arr.length == 2) {
column = arr[0]
val = arr[1]
op = '='
}
if (this.filter) {
this.filter += ` AND ${column} ${op} '${val}'`
} else {
this.filter = `WHERE ${column} ${op} '${val}'`
}
return this
}
where方法的具體實作其實就是把傳入的過濾字串拼裝filter變數里面,再最后做CURD操作時,將filter拼裝上去即可,
insert實作
insert 方法我希望像如下這樣使用:
let result = await orm.table('user').insert({
name:'張三',
age:18
})
console.log(result)
那么撰寫的時候,把物件遍歷完key和value,依次拼裝即可,
insert(obj) {
let keys = '',
values = ''
let objArr = Object.keys(obj)
for (let i = 0; i < objArr.length; i++) {
let key = objArr[i]
let val = obj[key]
if (i != objArr.length - 1) {
keys += `${key},`
//注意 value最好用''括起來
values += `'${val}',`
} else {
keys += `${key}`
values += `'${val}'`
}
}
this.sql = `INSERT INTO ${this._table} (${keys}) VALUES (${values})`
return this.run()
}
count方法
最后再貼一個count方法的實作吧
async count() {
let result = await this.select()
return new Promise((resolve, reject) => {
resolve(result.length)
})
}
其他的方法大同小異就不在贅述了,可以自行拓展一些更好的方法,拼裝好SQL之后呼叫run即可,
最后
行文至此,感謝閱讀,如果您喜歡的話,可以幫忙點個like喲~
該文章首發自【全堆疊web之路】,web開發之路,誠邀您攜手同行,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/153020.html
標籤:其他
上一篇:oracle資料庫語法總結
