从零开始的野路子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):

 

所有的请求都成功了,大功告成,这就是一个非常简陋的后端搭建数据库的示例。

代码见:

https://github.com/SilenceGTX/node_back_db

posted @ 2020-08-09 20:06  SilenceGTX  阅读(794)  评论(0编辑  收藏  举报