最近的項目,用nodejs做的webapi,連接sql server數據庫用的sequelize框架,現在把我自己簡單封裝的sequelize服務記錄下來,以備參考:
sql.js
const Sequelize = require('sequelize')
const fs = require('fs')
const path = require('path')
const basePath = path.resolve('./')
const db = {
database: 'TestMall',
username: 'sa',
password: 'whb',
host: '192.168.1.101',
port: 1433,
dialect: 'mssql',
// close log
logging: false,
// "timestamps: false" fixed Unknown column 'createdAt' in 'field list'
timestamps: false,
dialectOptions: {
multipleStatements: true
}
}
//const sequelize = new Sequelize('postgres://user:[email protected]:5432/dbname');
const sequelize = new Sequelize(db)
sequelize.sync()
sequelize.authenticate().then(() => {
console.log('Connection has been established successfully.')
}).catch(err => {
console.error('Unable to connect to the database:', err)
})
//sequelize.close()
// 統一事件調用方法,避免業務邏輯代碼裏大量重複的then()cathc()代碼
Sequelize.Model.excute = function (res, action, param, cb) {
this[action](param[0], (param.length > 1 ? param[1] : null)).then((re) => {
if (cb) {
cb(re)
} else {
res.json({
code: 200,
data: re
})
}
}).catch(err => {
console.log(err)
// 錯誤日誌
fs.writeFile(`${basePath}/logs/sqlerr.log`, err, () => {});
res.json({
code: 500,
data: err
})
})
}
// 統一存儲過程、批處理調用方法,避免業務邏輯代碼裏大量重複的then()cathc()代碼
Sequelize.Model.exec_proc = function (res, sql, param, cb) {
sequelize.query(sql, {
replacements: param
}).then(re => {
if (re[0][0].msg) {
// 錯誤日誌
fs.writeFile(`${basePath}/logs/sqlerr.log`, re[0][0].msg, () => {});
res.json({
code: 500,
data: re[0][0].msg
})
} else {
if (cb) {
cb(re[0])
} else {
res.json({
code: 200,
data: re[0]
})
}
}
}).catch(err => {
// 錯誤日誌
fs.writeFile(`${basePath}/logs/sqlerr.log`, err, () => { });
res.json({
code: 500,
data: err
})
})
}
module.exports = sequelize
考慮到新手不容易理解和接受,現在添加幾張數據表映射模型定義和幾個簡單調用例子:
model.js
const Sequelize = require('sequelize')
const sequelize = require('./sql')
const Model = Sequelize.Model
//#region 數據模型申明
class user extends Model {}
class goods extends Model {}
class files extends Model {}
class orders extends Model {}
//#endregion
//#region 數據模型定義
/**
* 用戶賬號信息表
*/
user.init({
id: {
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV1,
allowNull: false,
field: 'xGUID'
},
account: {
type: Sequelize.STRING,
allowNull: false,
field: 'Account'
},
name: {
type: Sequelize.STRING,
field: 'Name'
},
password: {
type: Sequelize.BOOLEAN,
allowNull: false,
field: 'Password'
}
}, {
sequelize,
timestamps: false,
tableName: 'Person'
})
/**
* 商品表
*/
goods.init({
id: {
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV1,
allowNull: false,
field: 'xGUID'
},
/**
* book's name
*/
name: {
type: Sequelize.STRING,
allowNull: false,
field: 'Name'
},
/**
* price
* 定價
*/
price: {
type: Sequelize.REAL,
allowNull: false,
field: 'DJ'
},
}, {
sequelize,
timestamps: false,
tableName: 'Goods'
})
/**
* 訂單表
*/
orders.init({
id: {
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV1,
allowNull: false,
field: 'xGUID'
},
quantity: {
type: Sequelize.INTEGER,
allowNull: false,
field: 'GoodsID'
},
amount: {
type: Sequelize.REAL,
allowNull: false,
field: 'Amount'
},
/**
* 外鍵, 關聯商品表主鍵
*/
goodsID: {
type: Sequelize.STRING,
allowNull: false,
field: 'GoodsID'
},
/**
* 外鍵, 關聯用戶表主鍵
*/
personID: {
type: Sequelize.STRING,
allowNull: false,
field: 'Person_ID'
}
}, {
sequelize,
timestamps: false,
tableName: 'Orders'
})
/**
* 資源文件表
*/
files.init({
id: {
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV1,
allowNull: false,
field: 'xGUID'
},
/**
* 外鍵, 關聯商品表主鍵
*/
goodsID: {
type: Sequelize.STRING,
allowNull: false,
field: 'LYBMID'
},
path: {
type: Sequelize.STRING,
allowNull: false,
field: 'Path'
}
}, {
sequelize,
timestamps: false,
tableName: 'File'
})
//#endregion
//#region 表關聯
goods.hasMany(files, {
as: 'images',
foreignKey: 'goodsID',
through: null
});
orders.belongsTo(user, {
as: 'buyer',
foreignKey: 'userID',
through: null
});
orders.belongsTo(goods, {
as: 'goods',
foreignKey: 'goodsID',
through: null
});
files.belongsTo(orders, {
as: 'order',
sourceKey: 'goodsID',
foreignKey: 'userID',
through: null
});
//#endregion
module.exports = {
user,
goods,
orders,
files
}
接下來是業務邏輯層的調用例子:
order.js
const express = require('express')
const Sequelize = require('sequelize')
const Op = Sequelize.Op
const {
user,
goods,
files,
orders
} = require('../models')
Router.post('/add', (req, res) => {
payInfo.exec_proc(res,"exec proc_addOrder :personID,:addressID,:amount,:goodsIDs,:nums", {
personID: req.body.personID,
goodsID: req.body.goodsID,
quantity: req.body.quantity,
amount: req.body.amount
},(re)=>{
if(re...){
//處理庫存鎖定等業務。。。
//存儲過程其實已經做了, 這裏就是表示下這個位置的用法。。。
}
})
})
Router.post('/list', (req, res) => {
orders.excute(res, 'findAndCountAll', [{
attributes: ['id', 'goodsID', 'buyerID', quantity, amount],
where: {
buyerID: req.body.userID,
status: {
[Op.ne]: '已取消'
}
},
include: [{
as: 'goods',
model: goods,
attributes: ['id', 'name', 'price'],
required: false
}, {
as: 'images',
model: files,
where: {
status: 0
},
attributes: ['id', 'name', 'path'],
required: false
}],
order: [
['createTime', 'DESC'],
],
offset: req.body.pageIndex * req.body.pageSize,
limit: req.body.pageSize
}])
})
module.exports = Router
最後是路由設定了:
route.js
const express = require('express')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const session = require('express-session')
const fs = require('fs')
const path = require('path')
//導入接口文件
const order = require('./controller/order')
// 默認頁,首頁
const pagePath = path.resolve(__dirname, 'public/index.html')
const page = fs.readFileSync(pagePath, 'utf-8', (err, data) => {
if (err) {
return err
}
return data
})
//創建express應用實例
const app = express()
//啓用session、cookie等
app.use(cookieParser())
app.use(
session({
secret: 'keyboard cat',
// cookie: {
// domain: '192.168.2.7',
// maxAge: 60 * 60 * 1000,
// secure: false
// },
resave: false,
saveUninitialized: true
})
)
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
extended: false
})
)
//設置跨域等頭部參數
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
//res.header('Access-Control-Allow-Origin', 'http://www.uni-engine.cn:8060')
res.header('Access-Control-Allow-Headers', 'Content-Type')
res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
res.header('Access-Control-Allow-Credentials', true);
res.header('X-Powered-By', ' 3.2.1')
res.header('Content-Type', 'application/json;charset=utf-8')
res.cookie('test', 'sadasdsad')
next()
})
//指定路由
app.use('/api/v1/order', order)
//允許訪問靜態頁
app.use('/apidoc', (req, res) => {
res.set('Content-Type', 'text/html')
res.send(page)
})
//允許訪問指定的靜態資源
app.use('/public', express.static(path.join(__dirname, 'public')))
//端口監聽
app.listen('9000', () => {
console.log('open Browser on http://127.0.0.1:9000')
})
這樣,一個相對完整的webapi程序的代碼就全有了。。。
下面再貼幾個常用寫法:
關聯查詢加分頁:
Router.post('/list', (req, res) => {
orders.findAndCountAll({
attributes: ['id', 'code', 'amount', 'status', 'createTime'],
where: {
personID: req.body.personID,
},
distinct: true, //避免因連接查詢導致數據重複,統計條數錯誤
include: [{
as: 'address',
model: receiveAddress,
attributes: ['id', 'area', 'address'],
required: false
}, {
as: 'details',
model: orderDetail,
attributes: ['id', 'status', 'quantity'],
include: [{
as: 'goods',
model: goods,
raw: true, //合併字段
attributes: ['desc', 'name', 'price', 'salePrice', 'stock'],
required: false //避免關聯表數據爲空是不返回主表數據
}, {
as: 'images',
model: files,
where: {
scenes: '圖書封面'
},
//through: false //如果多對多關係,中間有關聯表,這個屬性表示是否留存關聯表結構
raw: true, //合併字段
attributes: ['path'],
required: false //避免關聯表數據爲空是不返回主表數據
}]
}],
order: [
['createTime', 'DESC'],
],
offset: req.body.pageIndex * req.body.pageSize,
limit: req.body.pageSize
}).then((re)={
res.Json({
code: '200',
data: re
})
}).catch((err)=>{
res.Json({
code: '500',
data: err
})
})
})
通過調用存儲過程添加訂單:
Router.post('/add', (req, res) => {
payInfo.sequelize.query("exec proc_addOrder :personID,:addressID,:amount,:goodsIDs,:nums", {
personID: req.body.personID,
addressID: req.body.addressID,
amount: req.body.amount,
goodsIDs: req.body.goodsID,
nums: req.body.quantity
}).then((re) => {
res.json({
code: 200,
data: re[0]
})
}).catch(err => {
res.json({
code: 500,
data: err
})
})
})