一、传统数据库
数据库就是存储数据的,那么存储数据就用txt就行了啊,为什么要有数据库?
- 理由1: 数据库有行、列的概念,数据有关系,数据不是散的。
- 老牌数据库,比如MySQL、SQL Server、Oracle、Access,又叫做结构型数据库。
- 为什么?因为每个表中都有明确的字段,每行记录都有这些字段。不能有的行有,有的行没有。
- 理由2:数据库能够提供非常方便的接口,让增删改查操作变得简单
- 老牌数据库,都使用SQL语言,管理数据库。SQL就是structure query language。
- 理由3:数据库要给PHP、.net、jsp等语言提供接口。用php这些语言,能够向数据库中增删改查。
老牌数据库,都是结构型数据库,现在出了什么问题?
-
**数据不灵活。一个字段,需要是同样类型的数据。**不能一行记录是文本,一行记录是数字。也不能一行有字段,一行没有
-
关系表还支持约束
- 唯一的
- 主键
- 默认值
- 非空
非结构型数据库NoSQL应运而生。
NoSQL是个怪胎,无法挑战老牌数据库,但是在大数据时代有自己的意义。
二、NoSQL
特点 | NoSQL分类 | NoSQL数据库适用场景 |
---|---|---|
非结构型数据库 | 键值存储数据库 | 1、数据模型比较简单 |
没有行、列的概念。用JSON来存储数据 | 列存储数据库 | 2、需要灵活性更强的IT系统 |
集合就相当于“表”,文档就相当于“行” | 文档型数据库 | 3、对数据库性能要求较高 |
图形数据库 | 4、不需要高度的数据一致性 | |
5、对于给定key,比较容易映射复杂值的环境 |
三、MongoDB
3.1 基本概念
MongoDB是最像关系型数据库的非关系型数据库
- 数据库 ===》数据库
- 数据表 ===》集合(数组)
- 表记录 ===》(文档对象)
特点:
- 可以有多个数据库
- 一个数据库中可以有多个集合(表)
- 一个集合中可以有多个文档(表记录)
- 文档结构很灵活,没有任何限制
- MongoDB很灵活,不像MySQL一样先创建数据库、表、设计表结构
- 当自动插入数据的时候,只需要指定往哪个数据库的哪个集合操作就可以了
- 一切都由MongoDB来帮助自动完成建库建档
开机命令:mongod --dbpath c:\mongo
--dbpath就是选择数据库文档所在的文件夹。
mongoDB中,有物理文件对应一个个数据库,U盘可以拷贝。
开机这个CMD不能动,不能关,不能ctrl+c。 一旦这个cmd有问题了,数据库就自动关闭了。
所以,应该再开一个cmd。输入 mongo
那么,运行环境就是mongo语法了
命令 | 功能 |
---|---|
mongo | 使用数据库 |
exit | 退出连接 |
mongod | 开机 |
mongoimport | 导入数据 |
show dbs | 查看所有数据库列表 |
use 数据库名字 | 新建 / 使用某个数据库 |
db | 查看当前所在数据库 |
cls | 清屏 |
db.dropDatabase(); | 删除数据库,删除当前所在的数据库 |
3.2 增删改查
操作 | 示例 | 说明 |
---|---|---|
插入数据 | db.student.insert({'name': 'aa', 'age': 12}) | student就是所谓的集合。集合中存储着很多json。 student是第一次使用,集合将自动创建 |
mongoimport --db test --collection restaurants --drop --file primer-dataset.json | –db test 往哪个数据库导入 –collection restaurants 往哪个集合中导入 –drop 把集合清空 –file primer-dataset.json 哪个文件 | |
查找数据 | db.student.find({"score.shuxue":{$gt:50}, $or:[{"age":9},{"age":11}]}) | 没有参数将列出这个集合的所有文档db.student.find({}).limit(10).skip(page*10) 每页10条查找数学分数大于50,且年龄为9或11的学生 |
修改数据 | db.student.update({"sex":"男"},{$set:{"age":33}},{multi: true}); | 所有男性的年龄改为33岁,{multi: true} 表示更改所有匹配项目,否则只更改单个文档single document |
db.student.update({"name":"小明"},{"name":"大明","age":16}); | 完整替换,不出现$set关键字 | |
删除数据 | db.restaurants.remove( { "borough": "Manhattan" } ) | 默认移除匹配的所有文档 |
db.restaurants.remove( { "borough": "Queens" }, { justOne: true } ) | { justOne: true } 只移除匹配的一个文档 |
3.3 在Node中如何操作MongoDB数据
3.3.1使用官方的mongodb
包
npm搜安装包:https://www.npmjs.com/package/mongodb
跳转到github:https://github.com/mongodb/node-mongodb-native
有安装与使用规范,太原生了,一般不用
3.3.2 使用第三方mongoose
第三方包:mongoose
基于MongoDB官方的mongodb
包再一次做了封装
网址:https://mongoosejs.com/
四、Mongoose
4.1 概念
是一个将JavaScript对象与数据库产生关系的一个框架,object related model。
操作对象,就是操作数据库了;对象产生了,同时也持久化了。
这个思路是Java三大框架SSH中Hibernate框架的思路。彻底改变了人们使用数据库的方式。
-
官网:https://mongoosejs.com/
-
官方指南:https://mongoosejs.com/docs/guide.html
-
官方API文档:https://mongoosejs.com/docs/api.html
hello world:
var mongoose = require('mongoose'); // 引包,并不需要引用mongodb这个包
mongoose.connect('mongodb://localhost/test', { useMongoClient: true });// 链接数据库,test是数据库名字
mongoose.Promise = global.Promise;
// 创建一个模型,就是在设计数据库
// MongoDB 是动态的,非常灵活,只需要在代码中设计你的数据库就可以了
// mongoose 这个包就可以让你的设计编写过程变的非常的简单
var Cat = mongoose.model('Cat', { name: String });// 创建了一个模型。猫的模型。所有的猫都有名字,是字符串。“类”
for (var i = 0; i < 100; i++) {
// 实例化一个 Cat
var kitty = new Cat({ name: '喵喵' + i });
// 持久化保存 kitty 实例
kitty.save(function (err) {
if (err) {
console.log(err);
} else {
console.log('meow');
}
});
}
上面的代码中,没有一个语句是明显的操作数据库,感觉都在创建类、实例化类、调用类的方法,都在操作对象,但是数据库同步被持久了。
mongoose的哲学,就是让你用操作对象的方式操作数据库。
4.2 设计Schema发布model
创造schema → 定义一些schema的静态方法 → 创造模型
var mongoose = require('mongoose')
var Schema = mongoose.Schema
// 1.连接数据库
// 指定连接的数据库不需要存在,当你插入第一条数据之后就会自动被创建出来
mongoose.connect('mongodb://localhost/itcast')
// 2.设计集合结构(表结构)
// 字段名称就是表结构中的属性名称,约束的目的是为了保证数据的完整性,不要有脏数据
var userSchema = new Schema({
username:{
type:String,
required:true
},
password:{
type:String,
required:true
},
email:{
type:String
}
})
// 可创建静态方法:userSchema.statics.sayHi = ...
// 3.将文档结构发布为模型
// mongoose.model方法就是用来将用一个架构发布为model
// 第一个参数:传入一个大写名词单数字符串用来表示数据库名称
// mongoose会自动将大写名词的字符串生成 小写复数 的集合名称
// 例如这里的 User 最终会变成 users 集合名称
// 第二个参数:架构 Schema
// 返回值:模型构造函数
var User = mongoose.model('User', userSchema);
// 4.有了模型构造函数以后,使用这个构造函数对 users 集合中的数据进行操作(增删改查)
什么是静态方法,什么是类方法:
// 类
function Student(){}
// 实例化一个学生
var xiaoming = new Student();
// 实例方法,因为这个sleep方法的执行者是类的实例
xiaoming.sleep();
// 静态方法(类方法),这个方法的执行者是这个类,不是这个类的实例。
Student.findAllBuJiGe();
前台界面:不操作数据库,只操作类!
4.3 增删改查
操作 | 语法 | |
---|---|---|
增加数据 | var admin = new User({username:'admin'}); admin.save(function(err,ret){...}) | |
查询所有 | User.find(callback) | 如果中间有改过数据,会把更改前后的数据都给显示出来 |
按条件查询所有 | User.find({username:'admin'},callback) | |
按条件查询单个 | User.findOne({username:'admin'},callback) | 只查询一个,显示的是对象{},不是数组[] 不带条件的话,显示第一个对象,一般都会带条件的 |
删除数据 | User.remove({username:'admin'},callback) | |
根据条件更新所有 | Model.update(conditions,doc,[options],[callback]) | |
根据指定条件更新一个 | Model.findOneAndUpdate([conditions],[update],[options],[callback]) | |
根据id更新一个 | User.findByIdAndUpdate('id',{password:'123'}, callback) |
五、cookie、session
cookie是在res中设置,req中读取的。第一次访问没有cookie。
cookie的存储大小有限,4k。对用户可见,用户可以禁用、清除Cookie、可以被篡改。
cookie用来制作记录用户的一些信息
HTTP是无状态的协议,所以两次的访问,服务器不能认识到是同一个客户端的访问,就要用cookie来巧妙的解决这个问题。
Session就是利用cookie,实现的“会话”。就是第一次访问的时候,可以在服务器上为这个用户缓存一些信息,别的用户不能看见这个用户的信息。服务器会下发一个秘钥(cookie),客户端每次访问都携带这个秘钥,那么服务器如果发现这个秘钥吻合,就能够显示这个用户曾经保存的信息。
登陆就是用Session来制作的。任何语言的session都是透明的,不会体现cookie机理。
var session = require("express-session");
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))
app.get("/",function(req,res){
if(req.session.login == "1"){
res.send("欢迎" + req.session.username);
}else{
res.send("没有成功登陆");
}
});
app.get("/login",function(req,res){
req.session.login = "1"; // 设置这个session
req.session.username = "考拉"
res.send("你已经成功登陆");
});
都是使用req对象。
六、加密
永远不要用明码写密码
MD5加密是函数型加密。就是每次加密的结果一定相同,没有随机位。
特点:
-
不管加密的文字,多长多短,永远都是32位英语字母、数字混合
-
哪怕只改一个字,密文都会大变
-
MD5没有反函数破解的可能,网上的破解工具,都是通过字典的模式,通过大量列出明-密对应的字典,找到明码。
-
两次加密网上也有对应的字典,所以我们不要直接用一层md5,这样对黑客来说和明码一样。
MD5常用于作为版本校验。可以比对两个软件、文件是否完全一致。
node中自带了一个模块,叫做crypto模块,负责加密。
首先创建hash,然后update和digest:
var md5 = crypto.createHash('md5');
var password = md5.update(fields.password).digest('base64');
七、图片处理
http://www.graphicsmagick.org
只要服务器需要处理图片,那么这个服务器就要安装graphicsmagick软件,免费的。
装完之后,可视化工具一点用都没有,从桌面上删除。我们要把安装目录设置为环境变量。
控制台CMD命令:
gm convert a.bmp a.jpg
: 格式转换gm mogrify -resize 320x200 danny.jpg
:更改当前目录下*.jpg的尺寸大小,并保存于目录.thumb里面
node要使用graphicsmagick,需要npm装一个gm的包。
node缩略图的制作:
var fs = require('fs');
var gm = require('gm');
gm('./danny.jpg')
.resize(50, 50,"!")
.write('./danny2.jpg', function (err) {
if (err) {
console.log(err);
}
}
);
node头像裁切:
gm("./danny.jpg").crop(141,96,152,181).write("./2.jpg",function(err){
// 141 96 是宽高 152 181是坐标
});
总结
ejs模式:
// 写服务
app.get("/allstudent", function(req,res,next){
db.find("students", {}, function(err,result){
// 寻找完毕之后,result就是一个数组,服务已经成功了,但是,界面必须要可视化,所以要有模板引擎呈递
res.render("allstudent",{
"result" : result;
});
// 如果不用模板引擎呈递,可以直接输出JSON,前台用Ajax或者Angular调用
// var obj = {"result":result};
// res.json(obj);
});
});
<% for(var i = 0 ; i < result.length ; i++) {%>
<div class="grid">
<p class="title"><%=result[i].name%></p>
<p class="xuehao"><%=result[i].xuehao%></p>
<p class="sex"><%=result[i].sex%></p>
</div>
<%}%>
Ajax模式:
// 写服务
app.get("/allstudent", function(req,res,next){
db.find("students",{},function(err,result){
// 如果不用模板引擎呈递,可以直接输出JSON,前台用Ajax或者Angular调用
var obj = {"result":result};
res.json(obj);
});
});
======================================
<script type="text/template" id="tem">
// 这里是一个underscore模板
<div class="grid">
<p class="title">{{=name}}</p>
<p class="xuehao">{{=xuehao}}</p>
<p class="sex">{{=sex}}</p>
</div>
</script
前端页面: jQuery片段:
// 得到模板html
var templateString = $("#tem").html();
// ajax请求
$.get("/allsutdent",function(result){
// 这个result就是一个json对象
// 要放到页面上,所以为了不字符串拼接,可以用模板引擎underscore
for(var i = 0 ; i < result.result.length ; i++){
// 组装模板
var compiled = _.compiled(templateString,{
"name" : result.result[i].name;
"sex" : result.result[i].sex;
"xuehao" : result.result[i].xuehao;
});
// 上DOM
$(".liebiao").append($(compiled));
}
});
八、web Socket和Socket.IO框架
HTTP无法轻松实现实时应用:
-
HTTP协议是无状态的,服务器只会响应来自客户端的请求,但是它与客户端之间不具备持续连接。
-
我们可以非常轻松的捕获浏览器上发生的事件(比如用户点击了盒子),这个事件可以轻松产生与服务器的数据交互(比如Ajax)。
-
但是,反过来却是不可能的:服务器端发生了一个事件,服务器无法将这个事件的信息实时主动通知它的客户端。
-
只有在客户端查询服务器的当前状态的时候,所发生事件的信息才会从服务器传递到客户端。
但是,聊天室确实存在。
解决方法:
-
长轮询:客户端每隔很短的时间,都会对服务器发出请求,查看是否有新的消息,只要轮询速度足够快,例如1秒,就能给人造成交互是实时进行的印象。这种做法是无奈之举,实际上对服务器、客户端双方都造成了大量的性能浪费。
-
长连接:客户端只请求一次,但是服务器会将连接保持,不会返回结果(想象一下我们没有写res.end()时,浏览器一直转圈)。服务器有了新数据,就将数据发回来,又有了新数据,就将数据发回来,而一直保持挂起状态。这种做法的也造成了大量的性能浪费。
-
WebSocket
8.1 WebSocket
WebSocket协议能够让浏览器和服务器全双工实时通信,互相的,服务器也能主动通知客户端了。
● 原理:利用HTTP请求产生握手,HTTP头部中含有WebSocket协议的请求,所以握手之后,二者转用TCP协议进行交流(QQ的协议)。现在的浏览器和服务器之间,就是QQ和QQ服务器的关系了。
所以WebSocket协议,需要浏览器支持,更需要服务器支持。
● 支持WebSocket协议的浏览器有:Chrome 4、火狐4、IE10、Safari5
● 支持WebSocket协议的服务器有:Node 0、Apach7.0.2、Nginx1.3
Node上需要写一些程序,来处理TCP请求。
● Node从诞生之日起,就支持WebSocket协议。不过,从底层一步一步搭建一个Socket服务器很费劲(想象一下Node写一个静态文件服务都那么费劲)。所以,有大神帮我们写了一个库Socket.IO。
8.2 Socket.IO
Socket.IO是业界良心,新手福音。
- 屏蔽了所有底层细节,让顶层调用非常简单。
- 还为不支持WebSocket协议的浏览器,提供了长轮询的透明模拟机制。
Node的单线程、非阻塞I/O、事件驱动机制,使它非常适合Socket服务器。
下载库:npm install socket.io
写原生JS,搭建一个服务器,server创建好之后,创建一个io对象
var http = require("http");
var server = http.createServer(function(req,res){
res.end("你好");
});
var io = require('socket.io')(server);
// 监听连接事件
io.on("connection",function(socket){
console.log("1个客户端连接了");
})
server.listen(3000,"127.0.0.1");
写完这句话之后,你就会发现,http://127.0.0.1:3000/socket.io/socket.io.js 就是一个js文件的地址了。
现在需要制作一个index页面,这个页面中必须引用秘密js文件。调用io函数,取得socket对象。
<h1>我是index页面,我引用了秘密script文件</h1>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io();
</script>
此时,在服务器上,app.js中就要书写静态文件呈递程序,能够呈递静态页面。
var server = http.createServer(function(req,res){
if(req.url == "/"){
// 显示首页
fs.readFile("./index.html",function(err,data){
res.end(data);
});
}
});
至此,服务器和客户端都有socket对象了。
每一个连接上来的用户,都有一个socket。 由于我们的emit语句,是socket.emit()
发出的,所以指的是向这个客户端发出语句。
广播,就是给所有当前连接的用户发送信息:
// 创建一个io对象
var io = require('socket.io')(server);
// 监听连接事件
io.on("connection",function(socket){
console.log("1个客户端连接了");
socket.on("tiwen",function(msg){
console.log("本服务器得到了一个提问" + msg);
io.emit("huida","吃了");
});
});
复习顺序
-
Node.js特点:单线程、异步I/O(非阻塞I/O)、事件驱动(事件环)
-
适合的程序:就是没有太多的计算,I/O比较多的业务。举例:留言本、考试系统、说说、图片裁切服务器
-
Node.js原生: http、fs、path、url。 静态服务、简单路由、GET、POST请求。
-
模块:formidable、gm、express
-
Express:中间件、MVC建站、模板引擎ejs、静态服务、简单路由、GET、POST请求、MD5加密、图片上传。
服务器的一些概念:Cookie、Session
持久化NoSQL: 非关系型数据库,Not Only SQL。
- 特点:没有schema,没有行和列。用文档(JSON)来存储。
MongoDB:安装、开启、导入数据、Shell管理数据库、Mongo Vue、Node.js做CRUD(增删改查)、DAO层的封装、索引、操作符$set $lt $gt $push $pull
Mongoose: ODM,不用直接操作数据库,操作对象,这个对象自动持久。
Defining your schema 定义文档结构
schema定义的时候,支持的类型:String、Number、Date、Buffer、Boolean、Mixed、ObjectId、Array
转换为对象:mongoose.model(modelName, schema):
定义对象(实例)方法:
// define a schema
var animalSchema = new Schema({ name: String, type: String });
// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function (cb) {
return this.model('Animal').find({ type: this.type }, cb);
}
虚拟属性:
// define a schema
var personSchema = new Schema({
name: {
first: String,
last: String
}
});
// compile our model
var Person = mongoose.model('Person', personSchema);
// create a document
var bad = new Person({
name: { first: 'Walter', last: 'White' }
});