功能梳理完了以后,咱们就可以开始数据库表设计了:
数据库表图:
首先打开Navicat Premium 创建数据库 blog
配置如下:
课前学习:
1、Sequelize 中文API文档【强烈推荐】
2、Sequelize 和 MySQL 对照
3、使用Sequelize
4、Sequelize API
5、关于“时间”的一次探索
在blogNodejs/models 下
首先新建 mysql.js 进行mysql连接配置(基于Sequelize)
var config = require('config-lite');//引入灵活配置文件
var Sequelize = require('sequelize');.//引入Sequelize
var Mysql = new Sequelize(config.mysql.database, config.mysql.user, config.mysql.password, {
host: config.mysql.host, //数据库服务器ip
dialect: 'mysql', //数据库使用mysql
port: 3306, //数据库服务器端口
pool: {
max: 5,
min: 0,
idle: 10000
},
});
module.exports = Mysql;
然后根据数据库图,依次创建对应的Model
这里以user.js为示例 单独说下:
/**
* User 用户表
*/
var Sequelize = require('sequelize');//引入sequelize
var Mysql = require('./mysql');//引入mysql实例化
//定义User用户表
var User = Mysql.define('user', {
uuid: {//使用uuid 而不使用
type: Sequelize.UUID,//设置类型
allowNull: false,//是否允许为空
primaryKey: true,//主键
defaultValue: Sequelize.UUIDV1,//默认值
}, //uuid
email: { //邮箱
type: Sequelize.STRING,
allowNull: false,
unique: true, //唯一
validate: {//设置验证条件
isEmail: true,// 检测邮箱格式 (foo@bar.com)
},
},
password: { //密码
type: Sequelize.STRING,
allowNull: false,
},
state: { //状态 0未激活邮箱、1已激活邮箱
type: Sequelize.STRING(2),//限制字符个数
defaultValue: "0", //默认值
},
}, {
freezeTableName: true, //开启自定义表名
tableName: 'User',//表名字
timestamps: true, // 添加时间戳属性 (updatedAt, createdAt)
createdAt: 'createDate',// 将createdAt字段改个名
updatedAt: 'updateDate',// 将updatedAt字段改个名
indexes: [{ // 索引
type: 'UNIQUE', //UNIQUE、 FULLTEXT 或 SPATIAL之一
method: 'BTREE', //BTREE 或 HASH
unique: true, //唯一 //设置索引是否唯一,设置后会自动触发UNIQUE设置//true:索引列的所有值都只能出现一次,即必须唯一
fields: ['uuid'], //建立索引的字段数组。每个字段可以是一个字段名,sequelize 对象 (如 sequelize.fn),或一个包含:attribute (字段名)、length (创建前缀字符数)、order (列排序方向)、collate (较验的字段集合 (排序))
}],
comment:"User Table",//数据库表描述
});
module.exports = User;//导出
表都写完后,新建index.js
**
* 数据库表关系建立
*/
var Mysql = require('./mysql');
//表
var AdminUser = require('./adminUser');//管理员表
var User = require('./user');//用户表
var UserInfo = require('./userInfo');//用户信息表
var Article = require('./article');//文章表
var Category = require('./category');//文章类别表
var Attachment = require('./attachment');//文章附件表
/**
* 关系建立
*/
//用户-用户资料
User.hasOne(UserInfo); //1:1
//用户-文章
User.hasMany(Article); //1:N
Article.belongsTo(User); //1:1
//文章-分类 (定义中间表ArticleCategory 实现多对多)
Article.belongsToMany(Category,{through: 'ArticleCategory'}); //N:N
Category.belongsToMany(Article,{through: 'ArticleCategory'}); //N:N
//基于sequelize自动创建表//【!!注意 首次执行完请注释掉该段代码 !!】
Mysql.sync({
force: true,//是否清空数据库表
}).then(function() {
console.log('ok');
});
module.exports = {
AdminUser: AdminUser,
User: User,
UserInfo: UserInfo,
Article: Article,
Category: Category,
Attachment: Attachment,
};
好。到这里,咱们咱们打开
blogNodejs/app.js 写入以下代码
/**
* 主入口启动文件
* add by wwj
* 2017-08-24 15:01:48
*/
var express = require('express'); //web 框架
var logger = require('morgan'); //开发模式下log
var bodyParser = require('body-parser'); //json
var path = require('path'); //路径
var config = require('config-lite'); //读取配置文件
var winston = require('winston'); //日志
var expressWinston = require('express-winston'); //基于 winston 的用于 express 的日志中间件
var models = require('./models'); //临时添加 为了生成数据库表,后面写到Controllers里面
//实例化express
var app = express();
// 设置模板目录
app.set('views', path.join(__dirname, 'views'));
// 设置模板引擎为 ejs
app.set('view engine', 'ejs');
// log
app.use(logger('dev'));
//设置json
//格式化JSON的输出
app.set('json spaces', 2);
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({
extended: false
}));
// parse application/json
app.use(bodyParser.json());
// 设置静态文件目录
app.use(express.static(path.join(__dirname, 'public'))); //注意:中间件的加载顺序很重要。如上面设置静态文件目录的中间件应该放到 routes(app) 之前加载,这样静态文件的请求就不会落到业务逻辑的路由里;
//错误请求的日志
app.use(expressWinston.errorLogger({
transports: [
new winston.transports.Console({
json: true,
colorize: true
}),
new winston.transports.File({
filename: 'logs/error.log'
})
]
}));
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
//app
module.exports = app;
执行一下
npm run dev
然后去mysql 下看看是否创建成功了(右击“表”-刷新)
到这里,咱们的数据库已经ok啦
下面学习了解些业务逻辑写法
// 字段类型
// STRING
// CHAR
// TEXT
// INTEGER :A 32 bit integer.
// BIGINT :A 64 bit integer.
// FLOAT
// REAL
// DOUBLE
// DECIMAL
// BOOLEAN
// TIME
// DATE
// BLOB
//where
$and: {a: 5} // AND (a = 5)
$or: [{a: 5}, {a: 6}] // (a = 5 OR a = 6)
$gt: 6, // > 6
$gte: 6, // >= 6
$lt: 10, // < 10
$lte: 10, // <= 10
$ne: 20, // != 20
$not: true, // IS NOT TRUE
$between: [6, 10], // BETWEEN 6 AND 10
$notBetween: [11, 15], // NOT BETWEEN 11 AND 15
$in: [1, 2], // IN [1, 2]
$notIn: [1, 2], // NOT IN [1, 2]
$like: '%hat', // LIKE '%hat'
$notLike: '%hat' // NOT LIKE '%hat'
$iLike: '%hat' // ILIKE '%hat' (case insensitive) (PG only)
$notILike: '%hat' // NOT ILIKE '%hat' (PG only)
$like: { $any: ['cat', 'hat']}
// LIKE ANY ARRAY['cat', 'hat'] - also works for iLike and notLike
$overlap: [1, 2] // && [1, 2] (PG array overlap operator)
$contains: [1, 2] // @> [1, 2] (PG array contains operator)
$contained: [1, 2] // <@ [1, 2] (PG array contained by operator)
$any: [2,3] // ANY ARRAY[2, 3]::INTEGER (PG only)
$col: 'user.organization_id' // = "user"."organization_id", with dialect specific column identifiers, PG in this example
{
rank: {
$or: {
$lt: 1000,
$eq: null
}
}
}
// rank < 1000 OR rank IS NULL
{
createdAt: {
$lt: new Date(),
$gt: new Date(new Date() - 24 * 60 * 60 * 1000)
}
}
// createdAt < [timestamp] AND createdAt > [timestamp]
{
$or: [
{
title: {
$like: 'Boat%'
}
},
{
description: {
$like: '%boat%'
}
}
]
}
// title LIKE 'Boat%' OR description LIKE '%boat%'
CURD
create
result.dataValues;
========================
update
result [1];
========================
find - 从数据库中查找一个指定元素
- findById 按已知id查找
- findOne 按属性查找
result.dataValues
========================
findOrCreate - 从数据库中查找一个指定元素如果不存在则创建记录
========================
findAndCountAll - 从数据库中查找多个元素,返回数据与记录总数
count - 整数,匹配到的总记录数
rows - 对象数据,通过 limit 和 offset匹配的当前页数据
{ count: 0, rows: [] }
findAndCountAll同样支持使用include包含,使用包含时只有将required设置为true才会添加到count部分:
User.findAndCountAll({
include: [
{ model: Profile, required: true}
],
limit: 3
});
使用include时,两个模型之间应该存在主/外键关系,如果不存在就应该在include中手工建立连接。
在上面的示例中,为Profile设置了required,所以在查询时会使用INNER JOIN内连接。
========================
findAll - 从数据库中查找多个元素
// 查询时使用字符串替换
Project.findAll({ where: ["id > ?", 25] }).then(function(projects) {
// projects 是一个包含 Project 实例的数组,各实例的id 大于25
})
========================
count - 统计数据库中的元素数
max - 查找指定表中最大值
min - 查找指定表中最小值
sum - 对指定属性求和
========================
destroy
result
项目实战(提前了解,后面可以再学到controller那一层的时候看代码学习使用)
新增
const userResult = yield User.create({
email: email,
password: md5(password),
state: "1", //先默认已激活状态 //状态 0未激活邮箱、1已激活邮箱
});
更新
const userResult = yield User.update(user, {
// fields: ['trueName', 'birth', 'sex', 'city', 'address'], //设置允许更新字段白名单
where: {
uuid: userUuid,
},
});
yield User.update({
password: md5(newPwd),
}, {
// fields: ['password'], //设置允许更新字段白名单
where: {
uuid: userUuid,
},
});
查询单个
const userResult = yield User.findOne({
where: {
uuid: userUuid,
},
});
查询全部 模糊查询、分页、排序
var condition = {};
if (email) {
condition.email = {
'$like': '%' + email + '%',
};
}
if (trueName) {
condition.trueName = {
'$like': '%' + trueName + '%',
};
}
if(role){
condition.role = role;
}
//分页
var page = {
currPage: utils.handleCurrPage(params.currPage), //获取当前页
pageSize: utils.handlePageSize(params.pageSize), //每页数量
};
const result = yield User.findAndCountAll({
where: condition,
attributes: {
excludse: ['password'],
},
limit: page.pageSize, // 每页多少条
offset: page.pageSize * (page.currPage - 1), // 跳过多少条
order: [ //排序
['createDate', 'DESC'],
]
});
延伸:事务
场景:当表A数据入库成功后再去执行建立和表B的关联关系;这时候如果其中一个环节出了问题,那么整个操作就无效,而不是执行一半,造成脏数据;
//事务处理
return Mysql.transaction(function (t) {
// 先执行入库
return TableA.create(ajaxData, { transaction: t }).then(function(TableAResult){
// 入库成功后 如果TableB数据存在
if(TableBResult){
// 建立TableA与TableB的关系
return TableAResult.setTableBs(TableBResult, { transaction: t });
}
});
}).then(function () {// 事务已被提交 result 是 promise 链返回到事务回调的结果
//success
utils.handleJson({
response: res,
result: "ok",
});
}).catch(function(err) {
console.log(err);
utils.handleError({
response: res,
error: '失败',
});
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。