1传统数据库技术回顾
数据库就是存储数据的,那么存储数据就用txt就行了啊,为什么要有数据库?
理由之1: 数据库有行、列的概念,数据有关系,数据不是散的。
老牌数据库,比如MySQL、SQL Server、Oracle、Access。这些数据库,我们管他们叫做结构型数据库。为什么?因为每个表中,都有明确的字段,每行记录,都有这些字段。不能有的行有,有的行没有。
理由之2:数据库能够提供非常方便的接口,让增删改查操作变得简单
我们的老牌数据库,都无一例外的使用SQL语言,管理数据库。
SQL就是structure query language。
比如,查询所有女生: SELECT * FROM step1 WHERE xingbie = ‘女’;
再比如,查询所有女生,并且年龄20~24之间,且在北京:
SELECT * FROM step1 WHERE xingbie = '女' AND nianling < 24 AND nianling >= 20 AND xianzaisuozaidi = '北京';
理由之三:数据库不能自己玩儿,要给向PHP、.net、jsp等语言提供接口。
用php这些语言,能够向数据库之中增删改查。
老牌数据库,都是结构型数据库,现在出了什么问题?
比如,我们现在想往一个已经有1000条数据的数据库中增加一个字段“高中信息”。
之前已经存在的数据,实际上不需要增加这个字段。因为这些用户已经填写完毕表单了,不需要再手机高中信息了。我们的意图就是在今后注册的用户,需要填写高中信息。但是,我们刚才说了,所谓的字段,是表的一个结构。所有的行都必须拥有,不能有的行有这个字段,有的行没有这个字段。
可想而知,大数据时代,数据库中有100万条数据都算少的。我们如果要动字段,时间太长。
所以,字段这个东西,太不灵活。
数据不灵活。一个字段,需要是同样类型的数据。不能一行记录是文本,一行记录是数字。
非结构型数据库NoSQL应运而生。
NoSQL是个怪胎,无法挑战老牌数据库,但是在大数据时代有自己的意义。
2 NoSQL
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,是一项全新的数据库革命性运动,早期就有人提出,发展至2009年趋势越发高涨。NoSQL的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。
非结构型数据库。没有行、列的概念。用JSON来存储数据。
集合就相当于“表”,文档就相当于“行”。
1.数据库 DataBase
● 数据库中存储众多集合。
● 数据库最终会变为文件系统里面的文件,而数据库名字就是相应的文件名,所以数据库的命名,应该遵守操作系统的文件名命名规范。
● 数据库命名不能是admin、local、config
2.集合 Collections
集合就是一组文档,相当于“表”。
集合中可以存储完全不同结构的文档。
3.文档
NoSQL中,最小的“数据条目”,不是“行”,而是“文档”。
文档就是键值对的一个集合,实际上表达方式和JSON一样。
文档就是JSON,但是要比JSON多了一些限制:
● 每个文档必须有一个特殊的键 _id ,这个键在集合中必须是唯一的。
● 文档中的所有键不能重复;大小写不同的键,视为不同的键。
● 文档中键的命名,不能含有.和$,其他不限,甚至可以用中文命名、阿拉伯数字。
● 文档中值的类型,比JavaScript中多了一些,比如日期、ObjectId()、正则表达式。
● 文档给程序员看的时候,是JSON的表示模式,但是实际存储的时候,是BSON方式,即用二级制方式存储。
4.总结NoSQL的特点和优势
我们总结NoSQL数据库在以下的这几种情况下比较适用:
1、数据模型比较简单;
2、需要灵活性更强的IT系统;
3、对数据库性能要求较高;
4、不需要高度的数据一致性;
我们看,有些系统,特别需要筛选。比如,筛选出所有女生大于20岁的。那么SQL型数据库,非常擅长!因为它有行、列的概念。
但是,有些系统,真的不需要进行那么多的筛选,比如站内信。站内信只需要存储就好了。不需要筛选。那么NoSQL的。
NoSQL不是银弹,没有资格挑战老牌数据库,还是特定情况下,是适合的。
3 MongoDB
1. MongoDB基本安装
官网:https://www.mongodb.com/
手册:https://docs.mongodb.org/manual/
win7系统需要安装补丁,KB2731284。
此时,我们看一下装好的文件夹:
C:\Program Files\MongoDB\Server\3.0\bin 加入到系统的path环境变量中(这样就可以全局使用MongoDB命令了)
2. MongoDB基本使用
那么我们就能在系统的任何盘符,使用mongo命令了:
使用数据库: mongo
开机: mongod
导入数据: mongoimport
1.先开一个CMD:
开机命令:
–dbpath就是选择数据库文档所在的文件夹。(c:\mongo是自己在c盘下建立mongo,也就是说我们要自己创建数据库文档所在的文件夹)
也就是说,mongoDB中,真的有物理文件,对应一个个数据库。U盘可以拷走。
一定要保持,开机这个CMD不能动了,不能关,不能ctrl+c。 一旦这个cmd有问题了,数据库就自动关闭了。
2.再开一个CMD:
输入
那么,运行环境就是mongo语法了。
列出所有数据库:
show dbs
使用某个数据库
use 【数据库名字】
如果想新建数据库,也是use。use一个不存在的,就是新建。(例如新建use hcd)
查看当前所在数据库
db
插入数据:
student就是所谓的集合。集合中存储着很多json。
student是第一次使用,集合将自动创建。
3.实例:
1号cmd
开机:
mongod --dbpath D:\mongo
2号cmd
mongo //连接
show dbs //查看数据库
use hcd //新建hcd (如果你不插入数据,hcd的数据库是不会被创建的)
switched to db hcd
db //查看当前数据库
hcd
db.student.insert({"name":"hcd"}); //数据库中新建student集合,并插入数据
WriteResult({ "nInserted" : 1 })
show collections //显示所有的集合
student
system.indexes
db.student.find() //查看student集合的数据
{ "_id" : ObjectId("599c2fc210caa6deb9584e47"), "name" : "hcd" }
3. MongoDB视图软件 MongoVUE的使用
安装后打开
1.创建连接(要确定本地的mongoDB已经打开)
首先现按 +号(没有直接弹出框的话点左上角Connect按钮) 然后弹出窗口,name:自己想起的连接数据库的名字(不是数据库的名字),Server:127.0.0.1(本地服务地址),然后点击Save按钮
2.观察
假如:我们创建连接名字为first,数据库名字为test,集合名字books,集合中有两个文档,
按钮Tree View Table View Text View为查看文档的方式
4 MongoDB数据库的使用
(注意:这里是cmd的命令行方式,不用特别记住,还有Node方式)
可以查看手册https://docs.mongodb.org/manual/
1 插入数据
插入数据,随着数据的插入,数据库创建成功了,集合也创建成功了。
db.student.insert({"name":"xiaoming"});
我们不可能一条一条的insert。所以,我们希望用sublime在外部写好数据库的形式,然后导入数据库:
注意:mongoimport和mongo是同级的,不能在连接mongo后再执行:mongoimport命令
mongoimport --db test --collection restaurants --drop --file primer-dataset.json
-db test 想往哪个数据库里面导入
–collection restaurants 想往哪个集合中导入
–drop 把集合清空
–file primer-dataset.json 哪个文件
这样,我们就能用sublime创建一个json文件,然后用mongoimport命令导入,这样学习数据库非常方便。
例如:
mongoimport --db hcd --collection student --drop /c/Users/Administrator/Desktop/a.json
将数据存放在hcd数据库中的student的集合中,然后将json拖入命令工具中就行了
2 查找数据
查找数据,用find。find中没有参数,那么将列出这个集合的所有文档:
db.restaurants.find() //(列出restaurants集合的所有数据)
精确匹配:
db.student.find({"score.shuxue":70}); // {a:{b:‘c’}} 就用“a.b”:c表示11
多个条件:
db.student.find({"score.shuxue":70 , "age":12})
大于条件:
db.student.find({"score.yuwen":{$gt:50}});
寻找所有年龄是9岁,或者11岁的学生
db.student.find({$or:[{"age":9},{"age":11}]});
查找完毕之后,打点调用sort,表示升降排序。
db.restaurants.find().sort( { "borough": 1, "address.zipcode":-1 } )
//这里面表示先按照borough来升序排列,如果borough相同,则按照address.zipcode降序排列
3 修改数据
修改里面还有查询条件。你要该谁,要告诉mongo。
默认情况下更改第一个匹配的元素
查找名字叫做小明的,把年龄更改为16岁:
db.student.update({"name":"小明"},{$set:{"age":16}});
查找数学成绩是70,把年龄更改为33岁:
db.student.update({"score.shuxue":70},{$set:{"age":33}});
更改所有匹配项目:”
By default, the update() method updates a single document. To update multiple documents, use the multi option in the update() method.
db.student.update({"sex":"男"},{$set:{"age":33}},{multi: true});
完整替换,不出现$set关键字了:
db.student.update({"name":"小明"},{"name":"大明","age":16});
4 删除数据
默认情况下情况满足的都会删除
db.restaurants.remove( { "borough": "Manhattan" } )
By default, the remove() method removes all documents that match the remove condition. Use the justOne option to limit the remove operation to only one of the matching documents.
加上justOne: true表示只删除满足条件的第一个元素
db.restaurants.remove( { "borough": "Queens" }, { justOne: true } )
删除restaurants集合下的所有数据
db.restaurants.remove( {} )
5 Node.js操作MongoDB
首先:
Node.js安装MongoDB
npm install mongodb
API可以查看手册https://docs.mongodb.org/manual/
或者直接查看http://mongodb.github.io/node-mongodb-native/2.2/quick-start/quick-start/
案例1:
访问127.0.0.1/都会往数据库写入组对象
var express = require('express');
var app = express();
//在Node中引入mongodb模块
var MongoClient = require('mongodb').MongoClient;
//设置要链接的数据库URL,myproject为我们数据库的名字
var url = 'mongodb://localhost:27017/myproject';
//使用数据库连接服务
app.get('/',function(req,res){
// 回调函数中,err为错误,db为数据库
MongoClient.connect(url, function(err, db) {
//在数据库中简历集合'documents'
var collection = db.collection('documents');
//向集合中插入一组文档
collection.insertMany([
{a : 1}, {a : 2}, {a : 3}
], function(err, result) {
console.log("Inserted 3 documents into the collection");
});
//关闭数据库,正规的就是每一次操作都要打开和闭合数据库
db.close();
});
})
app.listen(3000);
结果:
04—案例01
案例2:
目录:
列出所有数据库中的数据
index.ejs:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
table,tr,td{
border:1px solid black;
border-collapse: collapse;
}
tr:nth-child(2n){
background-color: pink;
}
</style>
</head>
<body>
<h1>学生管理系统</h1>
<a href="/add">添加学生</a>
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>数学分数</th>
<th>语文分数</th>
</tr>
<% for(var i = 0 ; i < result.length ; i++){%>
<tr>
<td><%= result[i].name %></td>
<td><%= result[i].age %></td>
<td><%= result[i].score.yuwen %></td>
<td><%= result[i].score.shuxue %></td>
</tr>
<%}%>
</table>
</body>
</html>
add.ejs:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
</style>
</head>
<body>
<h1>学生管理系统</h1>
<form action="/tijiao" method="get">
<p>
姓名: <input type="text" name="name"/>
</p>
<p>
年龄: <input type="text" name="age"/>
</p>
<p>
数学成绩: <input type="text" name="shuxuechengji"/>
</p>
<p>
语文成绩: <input type="text" name="yuwenchengji"/>
</p>
<p>
<input type="submit"/>
</p>
</form>
</body>
</html>
Node.Js:
var express = require("express");
var MongoClient = require('mongodb').MongoClient;
var app = express();
//设置模板引擎
app.set("view engine","ejs");
//数据库连接的地址,最后的斜杠后面表示数据库名字
var shujukuURL = 'mongodb://localhost:27017/itcast';
app.get("/",function(req,res){
//先连接数据库,对数据库的所有操作,都要写在他的回调函数里面。
MongoClient.connect(shujukuURL, function(err, db) {
if(err){
res.write("数据库连接失败");
return;
}
//查询数据库,遍历所有数据到docs
db.collection('teacher').find({}).toArray(function(err, docs){
if(err){
res.write("遍历错误");
return;
}
res.render("index",{
"result" : docs
});
db.close();
});
});
});
app.get("/add",function(req,res){
res.render("add");
});
app.get("/tijiao",function(req,res){
//得到参数
var name = req.query.name;
var age = req.query.age;
var yuwenchengji = req.query.yuwenchengji;
var shuxuechengji = req.query.shuxuechengji;
MongoClient.connect(shujukuURL, function(err, db) {
if(err){
console.log("数据库连接失败");
return;
}
//将学生的数据存到数据库中
db.collection("teacher").insertOne({
"name" : name,
"age" : age,
"score" : {
"shuxue" : shuxuechengji,
"yuwen" : yuwenchengji
}
},function(err,result){
if(err){
console.log("数据库写入失败");
return;
}
res.send("恭喜,数据已经成功插入");
//关闭数据库
db.close();
});
});
});
app.listen(3000);
结果:
第一次访问http://127.0.0.1:3000/:
添加学生http://127.0.0.1:3000/add,并且填写相关的信息:
这时我们查看数据库:
这时我们再一次查看http://127.0.0.1:3000/会发现:
04—案例02
6封装DAO
开发DAO:J2EE开发人员使用数据访问对象(DAO)设计模式把底层的数据访问逻辑和高层的商务逻辑分开.实现DAO模式能够更加专注于编写数据访问代码.
使用我们自己的DAO模块,来实现数据库插入。代码变得简单。
我们这里把常用的增删改查,都封装成为module。(将API进行了简单的封装)
目录结构:
db.js就是我们要封装的js:
(下面的查找中包含了分页和总数,可以在7中查看)
//这个模块里面封装了所有对数据库的常用操作
var MongoClient = require('mongodb').MongoClient;
var settings = require("../settings.js");
//不管数据库什么操作,都是先连接数据库,所以我们可以把连接数据库
//封装成为内部函数
function _connectDB(callback) {
var url = settings.dburl; //从settings文件中,都数据库地址
//连接数据库
MongoClient.connect(url, function (err, db) {
if (err) {
callback(err, null);
return;
}
callback(err, db);
});
}
//插入数据
exports.insertOne = function (collectionName, json, callback) {
_connectDB(function (err, db) {
db.collection(collectionName).insertOne(json, function (err, result) {
callback(err, result);
db.close(); //关闭数据库
})
})
};
//查找数据,找到所有数据。args是个对象{"pageamount":10,"page":10}为第3个参数,为了防止有的时候
//可能会不传args而导致的传入的参数只有3个,这时第3个参数不再是args而是callback
exports.find = function (collectionName, json, C, D) {
if (arguments.length == 3) {
//那么参数C就是callback,参数D没有传。
var callback = C;
var skipnumber = 0;
//数目限制
var limit = 0;
} else if (arguments.length == 4) {
var callback = D;
var args = C;
//应该省略的条数,从第0页开始
var skipnumber = args.pageamount * args.page || 0;
//数目限制
var limit = args.pageamount || 0;
//排序方式
var sort = args.sort || {};
} else {
throw new Error("find函数的参数个数,必须是3个,或者4个。");
return;
}
//连接数据库,连接之后查找所有
_connectDB(function (err, db) {
db.collection(collectionName).find(json).skip(skipnumber).limit(limit).sort(sort).toArray(function(err, docs){
if (err) {
callback(err, null);
db.close(); //关闭数据库
return;
}
callback(null, result);
db.close(); //关闭数据库
})
});
}
//删除
exports.deleteMany = function (collectionName, json, callback) {
_connectDB(function (err, db) {
//删除
db.collection(collectionName).deleteMany(
json,
function (err, results) {
callback(err, results);
db.close(); //关闭数据库
}
);
});
}
//修改
exports.updateMany = function (collectionName, json1, json2, callback) {
_connectDB(function (err, db) {
db.collection(collectionName).updateMany(
json1,
json2,
function (err, results) {
callback(err, results);
db.close();
});
})
}
//获得数据的总数
exports.getAllCount = function (collectionName,callback) {
_connectDB(function (err, db) {
db.collection(collectionName).count({}).then(function(count) {
callback(count);
db.close();
});
})
}
Node.js:
var express = require("express");
var app = express();
var db = require("./model/db.js");
//插入数据,使用我们自己封装db模块,就是DAO。
app.get("/charu",function(req,res){
//三个参数,往哪个集合中增加,增加什么,增加之后做什么
db.insertOne("teacher",{"name":"小红"},function(err,result){
if(err){
console.log("插入失败");
return;
}
res.send("插入成功");
});
});
//查找
app.get("/du",function(req,res){
//这个页面现在接受一个page参数。
var page = parseInt(req.query.page); //express中读取get参数很简单
//查找4个参数,在哪个集合查,查什么,分页设置,查完之后做什么
db.find("canguan",{},{"pageamount":6,"page":page},function(err,result){
if(err){
console.log(err);
}
res.send(result);
console.log(result.length);
});
});
//删除
app.get("/shan",function(req,res){
var borough = req.query.borough;
db.deleteMany("canguan",{"borough":borough},function(err,result){
if(err){
console.log(err);
}
res.send(result);
});
});
//修改
app.get("/xiugai",function(req,res){
db.updateMany(
"canguan", //集合名字
{
"borough":"Manhattan" //改什么
},
{
$set: { borough: "北京" } //怎么改
},
function(err,result){ //改完之后做什么
if(err){
console.log(err);
}
res.send(result);
}
);
});
app.listen(3000);
7将查到的数据分页,数据总数
分页,想想我们的百度百家Ajax案例,当时调用了百度的JSON,有一个参数叫做page=3,生成的JSON不一样。
这个就是分页,就是我们想寻找所有的新闻,但是是位于第3页的新闻。那么有两种做法:
1) 错误的做法: 就是讲所有的result都读取到数组,然后进行数据操作,进行分页;
2) 正确的做法: 就是真的在数据库中,只读取这么多内容。
错误的,我们试图每次都读取全部数据,但是这样开销很大。
var a = [];
db.find("student",{},function(err,result){
for(var i = 10 * page ; i < 10 * (page + 1) ; i++){
a.push(result[i]);
}
res.send(a);
});
所以,mongodb提供了傻傻的两个函数。
limit() 表示找几条数据
skip() 表示基础条数,展示的是skip()基础上的limit()条
假如第一页是page=0。每页10条,所以当前页的查询语句:
db.student.find({}).limit(10).skip(page*10)
数据总数怎么得到?
shell中可以查看:
db.student.stats().count;
8索引index
数据库中,根据一个字段的值,来寻找一个文档,是很常见的操作。比如根据学号来找一个学生。
这个学号,是唯一的,只要有学号,就能唯一确认一个学生的文档。学号这个属性,就非常适合建立索引,这样一来,查找学生就变得简单了。
1.这个语句,能够查看检索的过程:
db.student.find({"name":"user888"});
2.学生的姓名是唯一的,为了快速的进行检索,所以就把name属性建立成为“索引”:
db.student.createIndex({"name":1});
这样,今后通过name寻找student文档的时候,速度非常快。因为能够快速的从索引表中,找到这个文档。
缺点就是插入每条数据的时候,时间变慢了,效率低了。但是换回来的就是寻找的速度快了。
3.索引这个属性对应的属性值,所有的文档都不能相同:
db.members.createIndex( { "user_id": 1 }, { unique: true } );
那么”user_id”不能相同
07—案例shuoshuo