技術(shù)交流 QQ 群:1027579432,歡迎你的加入!
歡迎關(guān)注我的微信公眾號:CurryCoder的程序人生
1.數(shù)據(jù)庫概述及環(huán)境搭建
1.1 為什么要使用數(shù)據(jù)庫
- 動態(tài)網(wǎng)站中的數(shù)據(jù)都是存儲在數(shù)據(jù)庫中。
- 數(shù)據(jù)庫可以用來持久存儲客戶端通過表單收集的用戶信息。
- 數(shù)據(jù)庫軟件本身可以對數(shù)據(jù)進行更高效的管理。
1.2 什么是數(shù)據(jù)庫
-
數(shù)據(jù)庫即存儲數(shù)據(jù)的倉庫,可以將數(shù)據(jù)進行有序的分門別類的存儲。它是獨立于語言之外的軟件,可以通過 API 去操作它。
Node.js與數(shù)據(jù)庫交互.png - 常見的數(shù)據(jù)庫軟件有:mysql、mongoDB、oracle、redis 等
1.3 mondoDB 數(shù)據(jù)庫下載安裝
- 下載地址:https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2012plus-4.2.8-signed.msi
- mongoDB 可視化操作工具:Robo 3T 1.2.1
1.4 數(shù)據(jù)庫相關(guān)概念
-
在一個數(shù)據(jù)庫軟件中可以包含多個數(shù)據(jù)倉庫,在每個數(shù)據(jù)倉庫中可以包含多個數(shù)據(jù)集合,每個數(shù)據(jù)集合中可以包含多條文檔(具體的數(shù)據(jù))。
數(shù)據(jù)庫相關(guān)概念.png
1.5 Mongoose 第三方包
- 使用 Node.js 操作 mongoDB 數(shù)據(jù)庫需要依賴 Node.js 第三方包 mongoose。
- 使用 npm install mongoose 命令下載安裝
1.6 啟動 mongoDB
- 在 cmd 中運行net start mongoDB即可啟動 mongoDB,否則 mongoDB 將無法連接。在 cmd 中運行net stop mongoDB即可關(guān)閉 mongoDB。
1.7 數(shù)據(jù)庫連接
-
使用 mongoose 提供的 connect 方法即可連接數(shù)據(jù)庫。
const mongoose = require("mongoose"); mongoose .connect("mongodb://localhost/CurryCoder", { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log("數(shù)據(jù)庫連接成功啦!")) .catch(err => console.log(err, "數(shù)據(jù)庫連接失敗啦!"));
1.8 創(chuàng)建數(shù)據(jù)庫
- 在 mongoDB 中不需要顯式創(chuàng)建數(shù)據(jù)庫,如果正在使用的數(shù)據(jù)庫不存在,mongoDB 會自動創(chuàng)建。
2.mongoDB 數(shù)據(jù)庫的增刪改查操作
2.1 創(chuàng)建集合
-
創(chuàng)建集合分為兩步:
- 首先,對集合設定規(guī)則。
- 然后,創(chuàng)建集合。創(chuàng)建 mongoose.Schema 構(gòu)造函數(shù)的實例即可創(chuàng)建集合。
const mongoose = require("mongoose"); mongoose .connect("mongodb://localhost/course_demo", { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log("數(shù)據(jù)庫連接成功")) .catch(err => console.log(err, "數(shù)據(jù)庫連接失敗")); // 設定集合規(guī)則 const courseSchema = new mongoose.Schema({ name: String, author: String, isPublished: Boolean }); // 使用規(guī)則去創(chuàng)建集合 // 參數(shù)1:集合名稱,參數(shù)2:集合規(guī)則 const Course = mongoose.model("Course", courseSchema); // 數(shù)據(jù)庫中集合名稱實際為courses2.2 創(chuàng)建文檔
-
創(chuàng)建文檔實際上是向集合中插入數(shù)據(jù)。分為兩步:
- 首先,創(chuàng)建集合的實例;
- 其次,調(diào)用實例對象下的 save()方法將數(shù)據(jù)保存到數(shù)據(jù)庫中。
- 和數(shù)據(jù)庫相關(guān)的操作都是異步操作
// 創(chuàng)建文檔--方法1
const course = new Course({
name: "JavaScript",
author: "CurryCoder",
isPublished: true
});
course.save();
// 創(chuàng)建文檔--方法2
Course.create(
{
name: "Python",
author: "Pink老師",
isPublished: true
},
(err, doc) => {
// 錯誤對象
console.log(err);
// 當前插入的文檔
console.log(doc);
}
);
// 創(chuàng)建文檔--方法2支持異步函數(shù)的語法
Course.create({
name: "C++",
author: "掃地僧",
isPublished: false
})
.then(doc => console.log(doc))
.catch(err => console.log(err))
2.3 mongoDB數(shù)據(jù)庫導入數(shù)據(jù)
- 前提:找到mongodb數(shù)據(jù)庫的安裝目錄,將安裝目錄下的bin目錄添加到系統(tǒng)的環(huán)境變量中。
- 導入數(shù)據(jù)語法:mongoimport -d 數(shù)據(jù)庫名 -c 集合名 --file 要導入的數(shù)據(jù)文件
E:\前端開發(fā)\JavaScript\練習代碼\mongoDB>mongoimport -d course_demo -c users --file ./user.json 2020-07-02T21:16:45.454+0800 connected to: localhost 2020-07-02T21:16:45.558+0800 imported 6 documents
2.4 查詢文檔
- 根據(jù)條件查找文檔(條件為空則查找所有文檔)
const mongoose = require("mongoose"); mongoose .connect("mongodb://localhost/course_demo", { // course_demo數(shù)據(jù)庫的名字 useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log("數(shù)據(jù)庫連接成功")) .catch(err => console.log(err, "數(shù)據(jù)庫連接失敗")); // 設定集合規(guī)則 const userSchema = new mongoose.Schema({ name: String, age: Number, email: String, password: String, hobbies: [String] }); // 使用規(guī)則去創(chuàng)建集合 // 參數(shù)1:集合名稱,參數(shù)2:集合規(guī)則 const User = mongoose.model("User", userSchema); // 數(shù)據(jù)庫中集合名稱實際為users // 查詢用戶集合中的所有文檔 User.find().then(result => console.log(result)); // 通過限制條件查找文檔 User.find({ _id: '5c09f267aeb04b22f8460968' }).then(result => console.log(result)); // findOne()方法返回一條文檔,默認返回當前集合中的第一條文檔 User.findOne({ name: '李四' }).then(result => console.log(result)); // 匹配大于 小于 User.find({ age: { $gt: 20, $lt: 50 } }).then(result => console.log(result)); // 匹配包含 User.find({ hobbies: { $in: ['敲代碼'] } }).then(result => console.log(result)); // 選擇要查詢的字段name email,不想查詢字段_id User.find().select('name email -_id').then(result => console.log(result)); // 將數(shù)據(jù)按照年齡進行降序排列 -age User.find().select('age -_id').sort('-age').then(result => console.log(result)); // skip跳過多少條數(shù)據(jù) limit限制查詢數(shù)量 User.find().skip(2).limit(3).then(result => console.log(result));
2.5 刪除文檔
- 刪除文檔如下:
const mongoose = require("mongoose"); mongoose .connect("mongodb://localhost/course_demo", { // course_demo數(shù)據(jù)庫的名字 useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log("數(shù)據(jù)庫連接成功")) .catch(err => console.log(err, "數(shù)據(jù)庫連接失敗")); // 設定集合規(guī)則 const userSchema = new mongoose.Schema({ name: String, age: Number, email: String, password: String, hobbies: [String] }); // 使用規(guī)則去創(chuàng)建集合 // 參數(shù)1:集合名稱,參數(shù)2:集合規(guī)則 const User = mongoose.model("User", userSchema); // 數(shù)據(jù)庫中集合名稱實際為users // 刪除單個文檔 // 如果查詢條件匹配了多個文檔,那么將會刪除第一個匹配的文檔 User.findOneAndDelete({ _id: '5c09f236aeb04b22f8460967' }) .then(result => console.log(result)); // 刪除多個文檔 User.deleteMany({}).then(result => console.log(result));
2.6 更新文檔
- 更新單個文檔:
User.updateOne({查詢條件}, {要更改的值}).then(result => console.log(result)); - 更新多個文檔:
User.updateMany({查詢條件}, {要更改的值}).then(result => console.log(result)); - 示例程序如下所示:
const mongoose = require("mongoose"); mongoose .connect("mongodb://localhost/course_demo", { // course_demo數(shù)據(jù)庫的名字 useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log("數(shù)據(jù)庫連接成功")) .catch(err => console.log(err, "數(shù)據(jù)庫連接失敗")); // 設定集合規(guī)則 const userSchema = new mongoose.Schema({ name: String, age: Number, email: String, password: String, hobbies: [String] }); // 使用規(guī)則去創(chuàng)建集合 // 參數(shù)1:集合名稱,參數(shù)2:集合規(guī)則 const User = mongoose.model("User", userSchema); // 數(shù)據(jù)庫中集合名稱實際為users // 更新集合中的單個文檔 User.updateOne({ name: '李四' }, { name: '葛二蛋' }).then(result => console.log(result)); // 更新集合中的多個文檔 // 如果查詢條件匹配了多個文檔,那么將會更新第一個匹配的文檔 User.updateMany({}, { age: 56 }).then(result => console.log(result));
2.7 mongoose驗證
- 在創(chuàng)建集合規(guī)則時,可以設置當前字段的驗證規(guī)則,驗證失敗就輸入插入失敗。
- required:true 必傳字段
- minlength:最小長度
- maxlength:最大長度
- min:數(shù)值最小為2
- max:數(shù)值最大為100
- enum: ['html', 'css', 'js']
- trim: 去除字符串兩邊的空格
- validate: 自定義驗證器
- message: 自定義錯誤信息
const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/course_demo', { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('數(shù)據(jù)庫連接成功')) .catch(err => console.log(err, '數(shù)據(jù)庫連接失敗')); // mongoose驗證 const postSchema = new mongoose.Schema({ title: { type: String, // required: true 必傳字段 // 驗證規(guī)則 required: [true, '請傳入文章標題'], // minlength: 2, minlength: [2, '文章長度不能小于2'], maxlength: 5, // 去除字符串兩端的空格 trim: true }, age: { type: Number, // 數(shù)字的最小范圍 min: 18, // 數(shù)字的最大范圍 max: [100, '年齡最大不超過100歲'] }, publishDate: { type: Date, // 默認值 default: Date.now }, category: { type: String, // 枚舉 列舉出當前字段可以擁有的值 enum: { values:['html', 'css', 'js', 'python'], message: '分類名稱要在指定的范圍內(nèi)才可以' } }, author: { type: String, validate: { validator: v => { // 返回布爾值 true表示驗證成功,反之表示失敗 v表示要驗證的值 return v && v.length > 4; }, // 自定義錯誤信息 message: '傳入的值不符合驗證規(guī)則' } } }); const Post = mongoose.model('Post', postSchema); Post.create({ title: 'aa', age: 30, category: 'C++', author: 'bd' }).then(result => console.log(result)).catch(error => { // 獲取錯誤信息對象 const err = error.errors; // 循環(huán)錯誤信息對象 for (let attr in err) { console.log(err[attr]['message']); } });
2.8 集合關(guān)聯(lián)
-
通常不同集合的數(shù)據(jù)之間是有聯(lián)系的,例如文章信息和用戶信息分別存儲在不同的集合中,但文章是某個用戶發(fā)表的,要查詢文章的所有信息包括發(fā)表用戶,就要考慮到集合關(guān)聯(lián)。
- 使用id對集合進行關(guān)聯(lián);
-
使用populate()方法進行關(guān)聯(lián)集合查詢
集合關(guān)聯(lián).png
const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/course_demo', { // course_demo數(shù)據(jù)庫的名字 useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('數(shù)據(jù)庫連接成功')) .catch(err => console.log(err, '數(shù)據(jù)庫連接失敗')); // 用戶集合規(guī)則 const userSchema = new mongoose.Schema({ name: { type: String, required: true } }); // 文章集合規(guī)則 const postSchema = new mongoose.Schema({ title: { type: String }, author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } }); // 用戶集合 const User = mongoose.model('User', userSchema); // 文章集合 const Post = mongoose.model('Post', postSchema); // 創(chuàng)建用戶 // User.create({name: 'CurryCoder'}).then(result => console.log(result)); // 創(chuàng)建文章 // Post.create({ title: '我的第一篇blog', author: '5efee0fd1dd5be06b4e24a87' }).then(result => console.log(result)); // 集合關(guān)聯(lián) Post.find({title: '我的第一篇blog'}).populate('author').then(result => console.log(result));
2.9 案例:用戶信息的增刪改查[代碼參見文末鏈接中的08_user文件夾]
- (1).搭建網(wǎng)站服務器,實現(xiàn)客戶端與服務端的通信;
- (2).連接數(shù)據(jù)庫,創(chuàng)建用戶集合,向集合中插入文檔;
- (3).當用戶訪問/list時,將所有用戶信息查詢出來;
- 實現(xiàn)路由功能;
- 程序用戶列表頁面;
- 從數(shù)據(jù)庫中查詢用戶信息,將用戶信息展示在列表中;
- (4).將用戶信息和表格HTML進行拼接,并將結(jié)果響應返回給客戶端;
- (5).當用戶訪問/add時,呈現(xiàn)表單頁面,并實現(xiàn)添加用戶信息的功能;
- (6).當用戶訪問/modify時,呈現(xiàn)修改頁面,并實現(xiàn)修改用戶信息的功能;
- 增加頁面路由呈現(xiàn)頁面
- 在點擊修改按鈕時,將用戶ID傳遞到當前頁面
- 從數(shù)據(jù)庫中查詢當前用戶的信息,將用戶信息展示到頁面中
- 實現(xiàn)用戶信息修改功能
- 指定表單的提交地址以及請求方式
- 接收客戶端傳遞過來的修改信息,找到用戶將用戶信息修改為最新的
- 增加頁面路由呈現(xiàn)頁面
- (7).當用戶訪問/delete時,實現(xiàn)用戶刪除功能;


