十多年前,高一的体育老师说过一句很每个时刻都会有所回味的话:
年轻靠爆发力,老了以后靠持久力。
之前的示例项目具有最明显的是:没有一个很好的持久化储存数据的途径。
本文介绍的是fs储存,mysql和sequelize。
现在就来完整实践一个fs-db操作库。需求如下:
// 根据属性获取数据
const getDataByProp = (prop) => {
return fs.readFile(file, (err, data) => {
if (!err) {
data = JSON.parse(data);
return data[prop];
} else {
console.log('读取失败!')
}
})
}
// 设置属性值
const setDataByProp = (prop, value) => {
fs.readFile(file, (err, data) => {
if (!err) {
data = JSON.parse(data);
data[prop] = value;
data = JSON.stringify(data);
fs.writeFile(file, data, (_err) => {
if (!_err) {
console.log('写入成功!')
} else {
console.log('写入失败')
}
})
} else {
console.log('读取失败')
}
})
}
读取和写入都比较简单。再实现一个命令行版的,允许命令行查询。
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on("line", input => {
const [op, key, value] = input.split(" ");
if (op === 'get') {
get(key)
} else if (op === 'set') {
set(key, value)
} else if (op === 'quit') {
rl.close();
} else {
console.log('没有该操作');
}
});
rl.on("close", function () {
console.log("程序结束");
process.exit(0);
});
用docker安装非常方便(Mac):
https://yeasy.gitbooks.io/docker_practice/install/mac.html
https://yeasy.gitbooks.io/docker_practice/container/run.html
sql语句对前端来说,看这个就足够了:
https://www.runoob.com/sql/sql-tutorial.html
安装mysql2,它提供了一套相当不错的ES7写法。
// 原生使用mysql
setTimeout(async () => {
const mysql = require('mysql2/promise');
const cfg = {
host: 'localhost',
user: 'root',
password: '12345678',
database: 'djtao'
}
const connection = await mysql.createConnection(cfg);
});
// sql语句
const CREATE_SQL = `CREATE TABLE IF NOT EXISTS test (
id INT NOT NULL AUTO_INCREMENT,
message VARCHAR(45) NULL,
PRIMARY KEY (id))`;
// 新建表
let ret = await connection.execute(CREATE_SQL);
console.log('create',ret)
打印出来了。发现新建了一个表
继续输入
// 插入表
const INSERT_SQL = `INSERT INTO test(message) VALUES(?)`;
ret = await connection.execute(INSERT_SQL,['hello']);
console.log('insert',ret);
新增了一条数据。id作为主键,是自增的。因此不需要理他。
继续输入
// 查询表
const SELECT_SQL = `SELECT * FROM test`;
const [rows,fileds] = await connection.execute(SELECT_SQL);
console.log('select',rows);
const UPDATE_SQL=`UPDATE test SET message='hi' WHERE id=1`
await connection.execute(UPDATE_SQL)
把id为1的message改为了hi
// 删除行
const DEL_SQL=`DELETE FROM test WHERE message='hello'`;
await connection.execute(DEL_SQL);
所有hello的都被删除了。
Sequelize是一款基于Nodejs功能强大的异步ORM框架。说白了就是对sql语句的封装。 同时支持PostgreSQL, MySQL, SQLite and MSSQL多种数据库,很适合作为Nodejs后端数据库的存储接口,为快速开发Nodejs应用奠定扎实、安全的基础。 既然Nodejs的强项在于异步,没有理由不找一个强大的支持异步的数据库框架,与之配合。
http://docs.sequelizejs.com/
// sequelize.js
(async ()=>{
const Sequelize=require('sequelize');
// 建立连接
const sequelize=new Sequelize('djtao',`root`,`12345678`,{
host:'localhost',
dialect:'mysql', //方言
operatorsAliases:false //操作符别名,不允许
});
// 6定义模型
const Fruit =sequelize.define('Fruit',{
name:{type:Sequelize.STRING(20),allowNull:false},
price:{type:Sequelize.FLOAT,allowNull:false},
stock:{type:Sequelize.INTEGER,defaultValue:0}
});
// 同步数据库
let ret=await Fruit.sync();
console.log('sync',ret)
})()
发现生成了一个数据表:
包括模型定义的三个字段,还有id和其它2个时间戳。
// 如果不想请求
const Fruit = sequelize.define("Fruit", {}, {
timestamps: false
});
定义模型后,就不用建表了。
插入数据呢?一行代码搞定:
await Fruit.create({name:'苹果',price:3.5});
就像操作对象一样操作数据库。
如果你要查询全部:
ret = await Fruit.findAll()
console.log('findAll',JSON.stringify(ret))
如果你想查询价格0-2.5的商品:
const Op=Sequelize.Op;
ret =await Fruit.FindAll({
where:{price:{[Op.lt]:0,[Op.gt]:2.5}}
})
await Fruit.update({price:'8.5'},{where:{name:'苹果'}})
// 方式1
Fruit.findOne({ where: { id: 1 } }).then(r => r.destroy());
// 方式2
Fruit.destroy({ where: { id: 1 } }).then(r => console.log(r));
删除数据库字段:通常不会操作已有的数据库。如果需要真的删除,则需要强制同步:
Fruit.sync({force: true})
price: {
validate: {
isFloat: { msg: "价格字段请输入数字" },
min: { args: [0], msg: "价格字段必须大于0" } }
},
stock: {
validate: {isNumeric: { msg: "库存字段请输入数字" }
}
}
// 添加类级别方法
Fruit.classify = function(name) {
const tropicFruits = ['香蕉', '芒果', '椰子']; // 热带水果
return tropicFruits.includes(name) ? '热带水果':'其他水果'; };
// 添加实例级别方法
Fruit.prototype.totalPrice = function(count) {
return (this.price * count).toFixed(2);
};
// 使用类方法
['香蕉','草莓'].forEach(f => console.log(f+'是'+Fruit.classify(f)));
// 使用实例方法
Fruit.findAll().then(fruits => {
const [f1] = fruits;
console.log(`买5kg${f1.name}需要¥${f1.totalPrice(5)}`);
});
以下是一个标准电商系统的ER图(实体关系与类模型),它反映出一对一或一对多映射关系
在这张图里,用户处于中心地位:一个以用户为中心的订单,最基本的要素包括六大类:
项目更目录下新建一个models模块,存放6个js文件对应六张表。建表不需要考虑外键。
// users.js
const Sequelize = require('sequelize');
const sequelize = require('../util/database');
const User = sequelize.define('user', {
id : {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
name: Sequelize.STRING,
email: Sequelize.STRING
});
module.exports = User;
// products.js
const Sequelize = require('sequelize');
const sequelize = require('../util/database');
const Product = sequelize.define('product', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
title: {
type: Sequelize.STRING,
allowNull: false
},
price: {
type: Sequelize.DOUBLE,
allowNull: false
},
imageUrl: {
type: Sequelize.STRING,
allowNull: false
},
description: {
type: Sequelize.STRING,
allowNull: false
}
});
module.exports = Product;
// cart.js
const Sequelize = require('sequelize');
const sequelize = require('../util/database');
const Cart = sequelize.define('cart', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false
}
});
module.exports = Cart;
// order.js
const Sequelize = require('sequelize');
const sequelize = require('../util/database');
const Order = sequelize.define('order', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
}
});
module.exports = Order;
// cart-item.js
const Sequelize = require('sequelize');
const sequelize = require('../util/database');
const CartItem = sequelize.define('cartItem', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
quantity: Sequelize.INTEGER
});
module.exports = CartItem;
// order-item.js
const Sequelize = require('sequelize');
const sequelize = require('../util/database');
const OrderItem = sequelize.define('orderItem', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
quantity: Sequelize.INTEGER
});
module.exports = OrderItem;
把数据库配置独立出来:
const Sequelize = require('sequelize');
const env = require('dotenv')
env.config();
const sequelize = new Sequelize(
process.env.DB_Database,
process.env.DB_USER,
process.env.DB_PWD, {
dialect: 'mysql',
host: 'localhost',
operatorsAliases: false
});
module.exports = sequelize;
数据建立需要初始化,那么可以写一个init函数:
以下展示了数据表初始化的过程.
setTimeout(async ()=>{
// 定义打印函数
const log=(text,data)=>{
console.log(`=======${text}========`);
console.log(JSON.stringify(data,null,'\t'));
console.log(`======================`);
};
const sequelize=require('./util/database');
// 定义1对N对模型关系
const Products=require('./models/products');
const Users=require('./models/users');
const Cart=require('./models/cart');
const CartItem=require('./models/cart-item');
const OrderItem=require('./models/order-item');
const Order=require('./models/order');
// 同步
await sequelize.sync({false:true});
})
// 产品表属于用户表
Products.belongsTo(Users,{
constraints:true,
onDelete:'CASCADE'//阻止删除
});
Users.hasMany(Products);
// 首要是创建用户,pk就是primarykey,对应的就是id
let user = await Users.findByPk(1);
if(!user){
user=await Users.create({
name:'djao',
email:'dangjingtao@163.com'
})
}
//添加商品
let product=await user.createProduct({
title:'iphone x',
price:999,
imageUrl:'iphonex.jpg',
description:'爱疯叉商品描述'
});
log('Product',product);
打印结果如下
// 产品表属于用户表
Products.belongsTo(Users, {
constraints: true,
onDelete: 'CASCADE'//阻止删除
});
Users.hasMany(Products);
// 首要是创建用户,pk就是primarykey,对应的就是id
let user = await Users.findByPk(1);
if (!user) {
user = await Users.create({
name: 'djao',
email: 'dangjingtao@163.com'
})
}
//添加商品
let product = await user.createProduct({
title: 'iphone x',
price: 999,
imageUrl: 'iphonex.jpg',
description: '爱疯叉商品描述'
});
那么对应对关系(外键)就建立起来了。
以上。