第七篇:koa2 web应用进阶—数据持久化

数据持久化 – 通俗地说就是将系统内存中的数据存储到数据库等数据管理工具中, 以及将数据从数据库中取出转换成内存中数据
本篇主要介绍 mysql, redis 两种数据管理工具在node中的使用。
本篇及以后的介绍都将基于第六篇建立的系统结构。

一、redis操作

redis的使用在第二篇 session 一节中有介绍过,本篇在此简单介绍下, 推荐使用node访问redis的中间件: ioredis, ioredis 的使用细节都可以在文档 (REDADME) 中找到。
1、redis连接: 为了避免每次使用redis都去写一段连接的代码,因此有必要 redis 建立链接的过程封装成一个公共 api, 方便使用。
a、首先在配置路径 /config 下的三套环境配置文件中添加 redis 配置数据:

1
2
3
4
5
6
 let redisConfig = {
port: 6379, //端口
host: '192.168.1.100', //地址
//password: 'auth', //访问密码
}
module.exports = { redisConfig: redisConfig } //导出

b、连接 redis API, 用于建立 node 与 redisd 的网络连接, 路径 ./src/lib/, 新建文件:redis-connection.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
'use strict'
const ioredis = require('ioredis') //引入 ioredis
//根据环境变量选择导入的配置文件
let redisConfig = require('../../config/dev.env.config').redisConfig
if( process.env.NODE_ENV === 'test' ) {
const redisConfig = require('../../config/test.env.config').redisConfig
} else if( process.env.NODE_ENV === 'production' ) {
const redisConfig = require('../../config/prod.env.config').redisConfig
}
//连接redis
let clientCreate = (config, callback_) => {
let redis = new ioredis(config)
redis.on('connect', () => { //根据 connect 事件判断连接成功
callback_(null, redis) //链接成功, 返回 redis 连接对象
})
redis.on('error', (err) => { //根据 error 事件判断连接失败
callback_(err, null) //捕捉异常, 返回 error
})
}
let redisConn = (options) => {
let config = options || redisConfig
return new Promise((resolve, reject) => { //返回API调用方 一个 promise 对象
clientCreate(config, (err, conn) => {
if(err) {
reject(err) //返回 err
}
resolve(conn) //返回连接对象
})
})
}
module.exports = redisConn

2、 使用上面写的API, 实现redis的增/删/改/查 路径 ./src/app/, 新建文件: redis/redis-test.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
'use strict'
const redisConn = require('../../lib/redis-connection.js')
class RedisTest {
constructor() {
this.redis = null
}
connToredis() { //创建连接对象
return new Promise((resolve, reject) => {
if(this.redis) {
resolve(true) //已创建
} else {
redisConn().then(resp => {
this.redis = resp
resolve(true) }
).catch(err => { reject(err) })
}
})
}
setCommand(id, data) { //增/改
if(expire === null || expire === undefined) {
this.redis.set(`test-${id}`, JSON.stringify(data)).then(resp => {
console.log('set', resp)
}).catch(err => {
console.log('err', err)
})
} else {
this.redis.set(`test-${id}`, JSON.stringify(data), 'ex', expire).then(resp => { //ex 为过期时间,单位为 秒
console.log('set', resp)
}).catch(err => {
console.log('err', err)
})
}
}
getCommand(id) { //查
this.redis.get(`test-${id}`).then(resp => {
console.log('get', resp)
}).catch(err => {
console.log('err', err)
})
}
delCommand(id) {
this.redis.del(`test-${id}`).then(resp => {
console.log('resp', resp)
})
}
multiCommand(id, data) {
this.redis.multi().set(`test-${id}`, JSON.stringify(data))
.get(`test-${id}`).exec((err, resp) => {
if(err) {
console.log('err', err)
} else {
console.log('resp', resp)
}
})
}
}
//调用上面的对象,实现redis操作
let redisTest = new RedisTest() //实例对象
redisTest.connToredis().then(resp => { //连接成功后,进行redis操作
if(resp) {
redisTest.setCommand(123456, {name: 'feifeiyu'}) //增
redisTest.getCommand(123456) //查
redisTest.setCommand(123456, {name: 'feifeiyu3'}) //改
redisTest.getCommand(123456)//查
redisTest.delCommand(123456) //删
redisTest.getCommand(123456) //差
redisTest.multiCommand(123457, {name: 'feifeiyu2'})
}
}).catch(err => {
console.log('err', err)
})
setTimeout(() => { //再进行一次redis操作
redisTest.connToredis().then(resp => {
redisTest.setCommand(123458, {name: 'feifeiyu'}, 1) //过期时间 1秒
setTimeout(() => { //等待过期
redisTest.getCommand(123458) //过期返回结果为null
}, 2000)
})
}, 2000)

返回结果
dao
redis 其他功能 订阅, pipeline等功能参见 文档 ioredis

二、mysql操作

MonogoDB 是node最常用的 noSQL 数据库,但是,目前来说 mysql 也是互联网公司使用最广泛的数据库之一。node 通过驱动 mysql 可以实现对mysql数据库的增、删、改、查操作。
安装: npm install –save mysql
注意: 以下内容之介绍node对mysql数据的基本操作,不涉及数据库建库建表等操作
1、 建立mysql连接, 一下只介绍myslq 的 连接池连接(pooling connection)
a、mysql 配置, 路径 ./config/*.env.config.js

1
2
3
4
5
6
7
8
9
10
//mysql连接池连接
let poolConfig = {
host: 'localhost',
port: '3306',
user: 'root',
password: 'mysql123',
database: 'fei',
// connectionLimit: 10,
}
module.exports = { poolConfig: poolConfig }

b、mysql 连接, 路径 ./src/lib/, 新建文件: mysql-connection.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
'use strict'
const mysql = require('mysql')
let mysqlConf = require('../../config/dev.env.config').poolConfig
if( process.env.NODE_ENV === 'test' ) {
const mysqlConf = require('../../config/test.env.config').poolConfig
} else if( process.env.NODE_ENV === 'production' ) {
const mysqlConf = require('../../config/prod.env.config').poolConfig
}
let poolCreate = (config, callback_) => {
const pool = mysql.createPool(config) //创建连接
pool.getConnection((err, conn) => {
if(err) {
callback_(err, null) //有错误 回调
} else {
console.log('get connected')
callback_(null, conn) //连接成功回调
}
})
}
let mysqlPool = (options) => {
let config = options || mysqlConf
return new Promise((resolve, reject) => {
poolCreate(config, (err, conn) => {
if(err) {
reject(err) //出错
}
resolve(conn) //成功
})
})
}
module.exports = mysqlPool

c、创建数据表 user
dao

d、dao层创建,即(实现 sql 语句操作), 路径 ./src/dao, 新建文件: mysql-dao.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  'use strict'
const mysqlPool = require('../lib/mysql-connection')
class MysqlTestDao {
constructor() {
this.fieldType = { //数据库中各个字段数据类型
id: 'INT',
nick: 'VARCHAR',
mobile: 'VARCHAR',
family_name: 'VARCHAR',
last_name: 'VARCHAR',
address: 'VARCHAR',
}
}
fieldTypeConfig(param) {
let keys = Object.keys(param)
keys.map(item => { //数据类型配置
try {
if(this.fieldType[item] == 'INT') {
param[item] = Number(param[item])
} else { //主要目的是为字符串数据加上引号
param[item] = '\'' + param[item] + '\''
}
} catch (err) {
return 'type error' + err
}
})
return param
}
queryAll() { //查询所有数据
return new Promise((resolve, reject) => {
mysqlPool().then( conn => {
let sql = 'select * from user;'
conn.query(sql, (err, result) => {
conn.release() //查询结束后释放链接池
if(err) reject(err)
resolve(result)
})
}).catch(err => {
reject(err)
})
})
}
queryById(param) { //根据id查询
return new Promise((resolve, reject) => {
mysqlPool().then( conn => {
let sql = 'select * from user where id=' + param.id + ';'
conn.query(sql, (err, result) => {
conn.release()
if(err) reject(err)
resolve(result)
})
}).catch(err => {
reject(err)
})
})
}
insert(param) { //插入数据
return new Promise((resolve, reject) => {
mysqlPool().then( conn => {
param = this.fieldTypeConfig(param)
if(typeof param === 'string') {
reject(param)
}
let keys = Object.keys(param)
let sql = 'insert into user '
+ '(nick, mobile, family_name, last_name, address) '
+ 'values '
+ '('
keys.map((item, index) => {
if(index === 0) {
sql += param[item]
} else {
sql += ', ' + param[item]
}
})
sql += ');'
conn.query(sql, (err, result) => {
conn.release()
if(err) reject(err)
resolve(result)
})
}).catch(err => {
reject(err)
})
})
}
updateById(param, id) { //更新数据
return new Promise((resolve, reject) => {
mysqlPool().then( conn => {
param = this.fieldTypeConfig(param)
if(typeof param === 'string') {
reject(param)
}
let keys = Object.keys(param)
let sql = 'update user set\ '
keys.map((item, index) => {
if(index === 0) {
sql += item + '=' + param[item]
} else {
sql += ', ' + item + '=' + param[item]
}
})
sql += 'where id=' + id + ';'
console.log('sql', sql)
conn.query(sql, (err, result) => {
conn.release()
if(err) reject(err)
resolve(result)
})
})
})
}
deleteById(id) { //删除数据
return new Promise((resolve, reject) => {
mysqlPool().then( conn => {
let sql = 'delete from user where id=' + id + ';'
console.log('sql', sql)
conn.query(sql, (err, result) => {
conn.release()
if(err) reject(err)
resolve(result)
})
})
})
}
}
module.exports = MysqlTestDao

e、 数据库使用(dao层调用),在业务逻辑目录 ./src/app/ 新建目录 mysqltest, 在 mysql 下新建 mysql-test.js, 加入一下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
'use strict'
const MysqlTestDao = require('../../dao/mysql-dao')
let mysqlTest = new MysqlTestDao()
var newItem = {
nick: 'feifeiyu3',
mobile: '13245654313',
family_name: 'xu',
last_name: 'lu',
address: 'hangzhou',
}
//查询 by id
mysqlTest.queryById({id: 1}).then(resp => {
console.log('query by id', resp)
}).catch(err => {
console.log('err', err)
})
//插入新数据
mysqlTest.insert(newItem).then(resp => {
console.log('inert', resp)
}).catch(err => {
console.log('err', err)
})
//查询所有
mysqlTest.queryAll().then(resp => {
console.log('queryAll', resp)
}).catch(err => {
console.log('err', err)
})
//数据更新
mysqlTest.updateById({nick: 'feifieyu5', mobile: 13256788765}, 2).then(resp => {
console.log('update by id', resp)
}).catch(err => {
console.log('err', err)
})
//数据删除
mysqlTest.deleteById(3).then(resp => {
console.log('delete by id', resp)
}).catch(err => {
console.log('err', err)
})

e 运行脚本,console 进入 mysql-test.js 所在目录, 运行命令 node –harmony mysql-test.js,即可看到相应的操作结果。

总结

本篇主要介绍了 redis 和 myslq 的 连接 API 封装, 数据的增、删、改、查。如果配合上 rest 接口(如 第四篇:koa2-web应用进阶—RESTful api),即可实现我们常用的 http 应用。如站点常见的,用户注册,注销,修改等。