从零开始的野路子React/Node(4)后端数据库
今天说说后端的数据库,各种app的搭建里数据库往往少不了,所以应该也是后端非常重要(可能最重要)的环节。
和之前一样,我们的后端还是采用node.js,node有一个常用ORM库叫Sequelize,用于间接操作数据库,这样一来我们不写SQL也可以进行各种数据库的操作了。
1、一些设置
首先,我们通过express database来创建一个名叫database的项目,然后我们通过npm install sequelize来安装Sequelize。安装完成后,我们还需要根据自己所用的数据库来安装数据库的驱动,这个可以见Sequelize的官网。假设我们使用Sqlite,那就npm install sqlite3。
和之前一样,我们删去bin目录,并修改package.json,把“start”对应的值改成nodemon app.js(记得先安装nodemon)。这样我们每次更改后端就会自动刷新了,不用重新启动。
我们修改一下app.js:
var express = require('express'); var app = express(); app.use(express.urlencoded({extended: true})); //解析json用 app.use(express.json()); //解析json用 app.listen(5000, function() { console.log('App listening on port 5000...') });
接下来我们看一下后端的总体结构(忽略node_modules, public和views):
其中app.js是我们后端的入口;
Routes负责后端的路由,针对不同的请求,找不同的Controller及其函数回应。就像拿到某种请求找对应的仓管一样;
Controllers负责处理请求并调用Model,每一个Controller相当于一个仓管;
Models负责实际执行具体的数据库操作(增删查改),相当于仓管的具体行为(取货,盘货);
Database则是数据库本体,定义了数据库中各个表的结构及不同表之间的交互,就相当于仓库本身。
这里还多了一个Prepopulate目录,这个目录主要是用来存放一些预先准备好的测试数据,并在后端启动时加载到数据库中。当然,这里的做法可能不是特别规范(规范的做法貌似应该用sequelize-cli)。
总体上来说,这个结构应该是比较符合MVC(Model-View-Controller)模式的。
2、建立数据库和表
现在我们来建立一个数据库并新建一张表,用于存放一些用户信息。
这里一共2个文件,index.js负责数据库的一些总体设置,以及插入我们预先准备好的测试数据。而User目录下的User.js则是用户信息表的结构(schema)等的设置。
我们首先看一下User.js:
const Sequelize = require('sequelize'); module.exports = (sequelize, DataTypes) => { class User extends Sequelize.Model {} User.init({ id: { type: DataTypes.INTEGER, //数据类型为整型 autoIncrement: true, //自增 allowNull: false, //不允许空值 primaryKey: true //主键 }, name: { type: DataTypes.STRING, allowNull: false }, user_group: { type: DataTypes.STRING, allowNull: false } }, { sequelize, modelName:'users', timestamps: true}); return User; }
我们的用户信息表一共3个字段,id是一个自增的主键(1,2,3,4…),name用于存放用户的名字,user_group用于存放用户所述的分组,最后我们把表命名为users。这里注意一下timestamps这个东西,timestamps其实默认就是true,也就是说sequelize会自动为我们的表创建2个字段createdAt和updatedAt,用于存放每条数据插入和更新的时间,如果你不需要的话,将其设置成false即可。
再来看看index.js:
const Sequelize = require('sequelize'); const sequelize = new Sequelize({ host: 'localhost', dialect: 'sqlite', storage: './database.sqlite' }); //设置数据库 var db = {}; var model = require('./User/User.js')(sequelize, Sequelize.DataTypes) db[model.name] = model; //将表装入数据库 db.sequelize = sequelize; db.Sequelize = Sequelize; module.exports = db;
我们先对数据库进行设置,这里我们用的是sqlite作为数据库,它最终会被保存在硬盘上一个叫database.sqlite的文件里,另外我们建立一个Object,db,将表都放入其中,方便我们之后调用。这样一来,db中就会有我们之前建立的users表。
3、插入测试数据
现在我们要向表中插入一些事先准备好的测试数据,以供我们可以测试后端的内容。
在Prepopulate下,Data目录用于存放需要插入的数据,一般都是json格式的,而InsertTables目录下的文件则用于执行插入数据的具体操作。
user.json的内容如下:
[ { "name":"张三", "user_group":"A" }, { "name":"李四", "user_group":"A" }, { "name":"王五", "user_group":"B" }, { "name":"马六", "user_group":"B" }, { "name":"冯七", "user_group":"C" } ]
InsertUser.js的内容如下:
module.exports = async function InsertForm(db) { var users = require('../Data/user.json') var userPromises = [] for (var user of users) { console.log(user); userPromises.push(await db['users'].create({ name: user.name, user_group: user.user_group }) .then(console.log('Data has been populated.')) .catch(err => {console.log(err)}) ) } return userPromises }
本质上就是从user.json中提取Object,然后一个个在users表中create出来。
完成之后,我们还需要再改一下Database目录下的index.js文件,让它执行插入数据的步骤:
const Sequelize = require('sequelize'); const sequelize = new Sequelize({ host: 'localhost', dialect: 'sqlite', storage: './database.sqlite' }); //设置数据库 var db = {}; var model = require('./User/User.js')(sequelize, Sequelize.DataTypes) db[model.name] = model; //将表装入数据库 //插入预置数据 sequelize.sync({force:true}) .then(() => { require('../Prepopulate/InsertTables/InsertUser')(db) }); db.sequelize = sequelize; db.Sequelize = Sequelize; module.exports = db;
4、加入针对表的操作
数据库中的表和测试数据我们都已经准备好了,现在可以正式进入现实增删查改的环节了。
我们在UserModel.js中实现之,由于sequelize的存在,增删查改一定程度上变得更简单了一些, 无须再写SQL了(当然你要写也是可以的)。
var db = require('../Database/index'); var { Sequelize, Op } = require('sequelize'); //获取所有用户 async function getAllUsers() { var users = await db.users.findAll() return users } //获取某个分组的用户 async function getGroupUsers(group) { var users = await db.users.findAll({ where:{ user_group:group } }) return users } //添加一个新用户 async function addUser(name, group) { var newUser = await db.users.create({ name:name, user_group:group }) return newUser } module.exports = { getAllUsers, getGroupUsers, addUser }
我们写了3个函数,2个用于查询,1个用于添加。第一个查询所有用户的函数,不需要任何参数;第二个查询某个分组所有用户的函数,需要一个参数group;第三个添加新用户,则需要2个参数。最后我们将它们导出,以便其他代码可以调用这些方法。
然后我们来添加Controllers:
Controller中的每个方法对应了Model里的相应方法。
var express = require('express'); var UserModel = require('../Models/UserModel'); //获取所有用户 exports.getAllUsers = (req, res) => { UserModel.getAllUsers() .then(response => { res.status(200).send(response) }) .catch(err => { console.log(err) res.status(400).send('Error') }) }; //获取某个分组的用户 exports.getGroupUsers = (req, res) => { UserModel.getGroupUsers(req.params.group) .then(response => { res.status(200).send(response) }) .catch(err => { console.log(err) res.status(400).send('Error') }) }; //添加一个新用户 exports.addUser = (req, res) => { UserModel.addUser(req.body.name, req.body.user_group) .then(response => { res.status(200).send(response) }) .catch(err => { console.log(err) res.status(400).send('Error') }) };
可以看到我们在此所做的事情不过是调用、传参、返回结果以及捕获错误。
此处需要说明的是,个人感觉在写Controllers之前,需要对每个方法所对应接受的请求有一个基本的设计。比如这里getAllUsers我希望它处理的是GET请求;我希望getGroupUsers也处理GET请求,但getGroupUsers需要一个参数(group),而GET请求只能从req.params中获取参数;最后addUser我希望它处理POST请求,而POST请求的参数则来自于req.body,这些都应当做好区分。
5、对接路由
现在我们可以将Controllers对接到路由系统上了。
通过写Controller时我们脑洞中的设计,我们可以脑补出每种请求对应的路径和请求类型(GET/POST):
var express = require('express'); const UserController = require('../Controllers/UserController'); var router = express.Router(); router.get('/:group', UserController.getGroupUsers); router.get('/', UserController.getAllUsers); router.post('/', UserController.addUser); module.exports = router;
我们将对应的请求类型、路径和对应的Controller的方法连接起来即可。
最后,我们修改一下app.js,把我们的路由系统加入进去:
var express = require('express'); const UserRouter = require('./Routes/User'); var app = express(); app.use(express.urlencoded({extended: true})); //解析json用 app.use(express.json()); //解析json用 app.use('/', UserRouter) app.listen(5000, function() { console.log('App listening on port 5000...') });
好啦,完工,现在我们来试试我们的后端系统。进入相应目录后通过npm start来启动后端,我们会发现两件事:
第一,根目录下多出了一个database.sqlite,如前所述,这是储存数据库的文件。
第二,我们可以看见后端启动后随之完成了一系列创建表、插入数据等操作。
让我们用Postman测试一下看看。
对”/”路径发送GET请求:
对”/:group”路径发送GET请求(req.params在路径中体现):
对”/”路径发送POST请求,参数在body中(JSON):
所有的请求都成功了,大功告成,这就是一个非常简陋的后端搭建数据库的示例。
代码见: