eggjs 构建后端博客系统(一),记录解决 sql 事务问题
废话部分
该项目git地址:https://github.com/ht-sauce/dream-admin
惯例:个人的废话部分
个人是前端开发,但是公司有时候比较闲。所以就想着自己写点东西,老摸鱼,太没意思了,还有一个就是装逼和加薪。
后端这块我比较早以前用过express,自己开发过简单的后端管理系统。
最近一年多正式做了开发,一开始我先搭建了我的前端博客页面(现在想起来是单页面项目,并不完美,最好是用服务端渲染来开发的)。后面博客系统肯定需要有后端所以,继续用nodejs是没错的。我不打算用java,或者go语言之类的。对于我这样的菜鸟,贪多嚼不烂。而且java是我见过最笨重的语言之一。java的开发很繁琐,历史包袱重。新手教程非常不友好。自学难度入门很难。进门之后又容易被不完美的教程带偏(缺这个少那个)。
java主要原因是因为早期没有一个包管理系统(目前的go语言等没有这个问题),导致了java在包管理上的混乱。导致新手不友好。
整体项目我是模拟大项目的。
项目目前分为四大块。
博客系统(个人生活展示平台,记录平台)
会员系统(负责登录和权限的统一管理,将登录和人员部分单独分离)
接口管理系统(就是一个接口记录平台)
后端管理平台
想法上非常美好,但是我目前为止后端才刚开始搞。前端基本页面差不多,但是没有后端的数据配合,写了总感觉缺少什么。虽然也能写啦,但是和后端不同,前端我写了一年多。感觉没有什么大挑战了。只是写的慢和快的问题,加上公司也不一定会有一些我设想中的东西。所以自己写后端,自己玩。(但是自己还是很懒,三个月才这么点)。
后端项目选型
一、框架选型
express肯定不会用了,所以后端我也就剩下koa框架了。(这块主要是参考招聘网站中常用的框架,而且结合市场去学习选型)
koa框架我官网看过,比express好很多。但是koa有种react的感觉,什么都需要自己弄。express也是一样的。
后面在逛论坛(cnode社区)下,发现cnode基本放弃express使用koa,jest和eggjs框架。其中eggjs是最多的。然后也去官网看了看。
eggjs最大的优点是他是阿里的,它用于双十一了。还有eggjs帮初学者和新手继承了非常多的功能。就像vue脚手架,初始就给你把架子搭建好了。你不再需要自己去做那些繁琐的事情,而且是业界大神帮你搭建的。
举例:
基础安全机制
日志系统
多进程负载
等……
其中多进程负载是我很看重的,因为这个新手老手都不一定能搞定。这是nodejs能利用多核cpu的关键。
二、基础学习
推荐我的这个文章看看,很好的入门视频教程。下载不了的请留言。我找时间自己上传百度云看看。
https://juejin.cn/post/6844903933324820493
三、插件选型
因为egg对后端的基础环境做了非常良好的集成。所以我们不再需要做繁琐的初始工作注重开发就行了。但是还是需要做基础的选型。主要是数据库连接上。
我说句不好听的,为什么后端程序员常常会被嘲讽为增删改查工程师。甚至是一些后端人员认为自己很牛逼(实际上只是会增删改查)。这不懂后端的大家不知道,懂了的会发现。后端入门之后基本上的事情就是连接数据库查询结果,插入数据,返回数据。就是这么简单会sql,甚至是部分代码只需要复制黏贴就行了。格式整齐划一。所以后端我认为主要牛逼在多年经验和算法。一个初级的后端程序员比前端简单,但是后端比前端工资高。为什么呢?因为前端没了后端走不了业务,后端可以离开前端(简单的页面)。其实非常不公平,如果两者分开,初级前端比初级后端强。特别是当下的前端环境。
说了这么多,其实所有的一切在初级后端下,我们只需要搞定框架和数据库连接插件就ok了。恩,还有写接口。
mongdb:egg-mongoose(也就是mongoose)
在数据库上面nodejs目前分为两大块,一个是mongdb和mysql。一个是nosql一个是关系型数据库。两者我都用过,但是在nosql上我表示自己不够熟练,还有就是在数据库建模方面总觉得有问题所以我放弃了。
那么只有mysql了。
mysql的选择不多,主要是mysql官方插件和orm插件(sequelize)
比较方面:
mysql:
egg-mysql:
就是传统的sql语句,主要缺点是开发人员容易发生数据库语句拼接,发生sql注入。
egg-sequelize
很多人说性能问题,这个可以忽略不计。最大问题在于非关系型语句操作。全部是封装好的对象编写方式。关键在于学习了sql语句之后还需要学习这个。但是不得不说,orm框架简化了一些sql的编写问题。
接口整合方面:
graphql:
非常不喜欢,文档少,而且繁琐。总感觉目前而已意义不大。虽然很诱人的感觉。
四、连接数据库。
基础的数据库安装我就不教了。我用mysql8.X
数据库连接很简单,egg封装上都做好了。
先执行
npm install --save egg-sequelize mysql2
然后配置数据库连接
\dream-admin\config\plugin.js
配置插件
'use strict';
/** @type Egg.EggPlugin */
module.exports = {
static: {
enable: true,
},
sequelize: {
enable: true,
package: 'egg-sequelize',
},
};
配置数据库连接(mysql时间格式转化也写好了。看配置)
\dream-admin\config\plugin.js\config.default.js
/* eslint valid-jsdoc: "off" */
'use strict';
module.exports = appInfo => {
const config = exports = {
static: { prefix: '/dreamdht/' },
keys: appInfo.name + '_1568770372144_7988',
security: {
csrf: {
ignore: () => true,
},
},
};
config.sequelize = {
dialect: 'mysql', // support: mysql, mariadb, postgres, mssql
database: 'dream',
host: 'localhost',
port: 9906,
username: 'root',
password: '1111',
timezone: '+08:00',
// 时间格式转化
dialectOptions: {
dateStrings: true,
typeCast: true,
},
};
return config;
};
五、写模型
orm框架和传统数据库插件相比,多了模型的编写这一步。相当于数据库建表,建模。
egg框架所有业务模型都放在app\model文件下面
我的是这样的
拿出一个模型例子:userInfo
这里我不做多余延伸。因为这篇文章主要是记录数据库操作的问题。整篇文章涉及很多知识点。每一项都需要很大的精力去阅读文档。特别是没有后端基础那更加是看不了的。
'use strict';
module.exports = app => {
const { STRING, INTEGER, DATE, ENUM } = app.Sequelize;
const UserInfo = app.model.define('consumer-userInfos', {
userid: {
type: INTEGER,
primaryKey: true,
comment: 'user账号表id',
},
nickname: {
type: STRING(30),
allowNull: false,
defaultValue: '',
comment: '昵称',
},
portrait: {
type: STRING(200),
comment: '用户头像',
},
sex: {
type: ENUM('男', '女'),
comment: '性别',
},
phone: {
type: STRING(15),
comment: '联系电话',
unique: true,
},
birthday: {
type: DATE,
comment: '生日',
},
provinceAndCity: {
type: STRING(30),
comment: '省市区域码',
},
address: {
type: STRING(100),
comment: '详细住址',
},
qq: {
type: STRING(100),
comment: 'qq或微信信息',
},
email: {
type: STRING(50),
comment: '邮箱地址',
unique: true,
},
}, { comment: '用户信息表,每个用户唯一' });
return UserInfo;
};
六、服务编写(service层,业务逻辑编写全部在这里)
这里一样说文章主要目的。
下面的语句包含两部分,一个是sql事务,还有一个是关于现代nodejs框架配合async\await如何处理错误。
sql事务,主要看注释。我之前犯错主要在这里
await ctx.model.Consumer.UserInfo.create(data, { transaction });
错误的方式:
await ctx.model.Consumer.UserInfo.create(data, transaction );
错误处理
eggjs在错误处理上和传统的后端很像了,主要是靠try\catch捕获错误。
然后我这里的逻辑调用在于我是控制层访问——服务层——访问——数据库
那么服务层其实就能捕获数据库错误了。但是在控制层我一样是用try\catch来捕获错误。那么你应该在我下面代码一样。用
// 返回错误信息
return Promise.reject(e);
// 或throw e;
来返回错误结果,保证控制层也正常的报错
直接上代码:
'use strict';
const Service = require('egg').Service;
class UserService extends Service {
async find() {
const { ctx, app } = this;
const sequelize = app.Sequelize;
const query = [[ sequelize.fn('COUNT', sequelize.col('id')), 'num' ]];
// console.log(result[0].dataValues.num);
return await ctx.model.Consumer.User.findAll({
attributes: query,
});
}
// 创建用户账号和用户信息
async create(data) {
// 获取当前条目数加1作为主键id
const { ctx, app } = this;
const sequelize = app.Sequelize;
const query = [[ sequelize.fn('COUNT', sequelize.col('id')), 'num' ]];
const count = await ctx.model.Consumer.User.findAll({
attributes: query,
});
const id = count[0].dataValues.num + 1;
data.id = id;
data.userid = id;
let transaction;
try {
// 启用事务
transaction = await ctx.model.transaction();
// 创建账号
await ctx.model.Consumer.UserInfo.create(data, { transaction });
await ctx.model.Consumer.User.create(data, { transaction });
// 提交事务
await transaction.commit();
return true;
} catch (e) {
// 错误事务回滚
await transaction.rollback();
// 返回错误信息
return Promise.reject(e);
}
}
}
module.exports = UserService;
七:总结
文章主要三点:
Sequelize下面事务的使用。格式很标准。
// 启用事务
transaction = await ctx.model.transaction();
// 创建账号
sql业务操作
//提交事务
transaction.commit();
当发生错误的时候进行回滚
await transaction.rollback();
然后是事务使用的时候注意事项。代码格式别错了。
还有就是mysql时区导致查询出来的结果时间格式不对
八、致谢
感谢eggjs团队开源框架
参考文章:
sequelize中文文档:https://demopark.github.io/sequelize-docs-Zh-CN/
参考博客:https://blog.****.net/awhlmcyn/article/details/79816494
参考博客:https://blog.****.net/clearlxj/article/details/94597734
上一篇: Fofa 查询脚本
下一篇: 使用着色器效果实现雨滴落水效果!