数据库概念&环境搭建
目标
- 能够安装数据库软件
- 能够知道集合、文档的概念
- 能够使用mongoose创建集合的方法创建集合
为什么要使用数据库(★★★)
- 动态网站中的数据都是存储在数据库中的
- 数据库可以用来持久存储客户端通过表单收集的用户信息
- 数据库软件本身可以对数据进行高效的管理
什么是数据库(★★★)
- 数据库即存储数据的仓库,可以将数据进行有序的分门别类的存储。它是独立于语言之外的软件,可以通过API去操作它。
- 常见的数据库软件有:mysql、mongoDB、oracle。
MongoDB数据库下载安装(★★★)
下载地址:https://www.mongodb.com/download-center/community
[外链图片转存失败(img-HRsJ9Brw-11)(images/mongodb.png)]
MongoDB可视化软件
MongoDB可视化操作软件,是使用图形界面操作数据库的一种方式。
[外链图片转存失败(img-xF7kGxMN-12)(images/sql-keshihua.png)]
数据库相关概念(★★★)
在一个数据库软件中可以包含多个数据仓库,在每个数据仓库中可以包含多个数据集合,每个数据集合中可以包含多条文档(具体的数据)。
| 术语 | 解释说明 |
|---|---|
| database | 数据库,mongoDB数据库软件中可以建立多个数据库 |
| collection | 集合,一组数据的集合,可以理解为JavaScript中的数组 |
| document | 文档,一条具体的数据,可以理解为JavaScript中的对象 |
| field | 字段,文档中的属性名称,可以理解为JavaScript中的对象属性 |
Mongoose第三方包
- 使用Node.js操作MongoDB数据库需要依赖Node.js第三方包mongoose
- 使用
npm install mongoose命令下载
启动MongoDB
在命令行工具中运行net start mongoDB即可启动MongoDB,否则MongoDB将无法连接。
MongoDB增删改查(CLUD)操作
目标
- 能够对数据库中的数据进行增删改查操作
数据库连接(★★★)
使用mongoose提供的connect方法即可连接数据库。
// 引入mongoose第三方模块 用来操作数据库 const mongoose = require('mongoose'); // 数据库连接 mongoose.connect('mongodb://localhost/playground', {
useNewUrlParser: true}) // 连接成功 .then(() => console.log('数据库连接成功')) // 连接失败 .catch(err => console.log(err, '数据库连接失败'));
讯享网
创建集合(创建表)(★★★)
创建集合分为两步,一是对对集合设定规则,二是创建集合,创建mongoose.Schema构造函数的实例即可创建集合。
讯享网 // 设定集合规则 const courseSchema = new mongoose.Schema({
name: String, author: String, isPublished: Boolean }); // 创建集合并应用规则 const Course = mongoose.model('Course', courseSchema); // courses
创建文档(插入数据)(★★★)
创建文档实际上就是向集合中插入数据。
分为两步:
- 创建集合实例。
- 调用实例对象下的save方法将数据保存到数据库中。
// 创建集合实例 const course = new Course({
name: 'Node.js course', author: '黑马讲师', tags: ['node', 'backend'], isPublished: true }); // 将数据保存到数据库中 course.save();
插入数据另外一种方法
讯享网//写法一 Course.create({
name: 'JavaScript基础', author: '黑马讲师', isPublish: true}, (err, doc) => {
// 错误对象 console.log(err) // 当前插入的文档 console.log(doc) }); //写法二 Course.create({
name: 'JavaScript基础', author: '黑马讲师', isPublish: true}) .then(doc => console.log(doc)) .catch(err => console.log(err))
mongoDB数据库导入数据
找到mongodb数据库的安装目录,将安装目录下的bin目录放置在环境变量中
[外链图片转存失败(img-IEu9Td2C-14)(images/mongodb-path.png)]
mongoimport –d 数据库名称 –c 集合名称 –-file 要导入的数据文件
mongoimport -d test -c users --file 数据文件的路径
查询文档(数据)(★★★)
利用find的方法查询
查询所有(★★★)
讯享网//定义好表规则 const user = new mongodb.Schema({
_id: String, username: String, password: Number, age: Number, name: String, hobbies: [String], email: String }); //生成对应的对象索引 const userEntity = mongodb.model('User', user); // 根据条件查找文档(条件为空则查找所有文档) Course.find().then(result => console.log(result)) // 返回文档集合 [{
_id: 5c0917ed37ec9b03c07cf95f, name: 'node.js基础', author: '黑马讲师‘ },{
_id: 5c09dea28acfbff827, name: 'Javascript', author: '黑马讲师‘ }]
根据条件查询(★★★)
//定义好表规则 const user = new mongodb.Schema({
_id: String, username: String, password: Number, age: Number, name: String, hobbies: [String], email: String }); //生成对应的对象索引 const userEntity = mongodb.model('User', user); // 根据条件查找文档 userEntity.findOne({
name: 'node.js基础'}).then(result => console.log(result)) // 返回文档 {
_id: 5c0917ed37ec9b03c07cf95f, name: 'node.js基础', author: '黑马讲师‘ }
多条件查询(★★)
根据范围查询
讯享网// 匹配大于 小于 userEntity.find({
age: {
$gt: 20, $lt: 50}}).then(result => console.log(result))
包含某字符
// 匹配包含 userEntity.find({
hobbies: {
$in: ['敲代码']}}).then(result => console.log(result))
查询某一个字段
讯享网// 选择要查询的字段 userEntity.find().select('name email').then(result => console.log(result))
排序查询
// 将数据按照年龄进行排序 升序 userEntity.find().sort('age').then(result => console.log(result)) // 将数据按照年龄进行排序 降序 userEntity.find().sort('-age').then(result => console.log(result))
分页查询
讯享网// skip 跳过多少条数据 limit 查询几条数据 userEntity.find().skip(2).limit(2).then(result => console.log(result))
删除文档(★★★)
删除单条数据
//定义好表规则 const user = new mongodb.Schema({
_id: String, username: String, password: Number, age: Number, name: String, hobbies: [String], email: String }); //生成对应的对象索引 const userEntity = mongodb.model('User', user); // 删除单个 userEntity.findOneAndDelete({
}).then(result => console.log(result))
删除多个数据
讯享网//定义好表规则 const user = new mongodb.Schema({
_id: String, username: String, password: Number, age: Number, name: String, hobbies: [String], email: String }); //生成对应的对象索引 const userEntity = mongodb.model('User', user); // 删除多个 userEntity.deleteMany({
}).then(result => console.log(result))
更新文档(★★★)
更新单条数据
// 更新单个 User.updateOne({
查询条件}, {
要修改的值}).then(result => console.log(result))
更新多条数据
讯享网// 更新多个 User.updateMany({
查询条件}, {
要更改的值}).then(result => console.log(result))
mongoose验证(★★)
在创建集合规则时,可以设置当前字段的验证规则,验证失败就则输入插入失败。说白了,就是规定我们插入数据库每一项的条件
- required: true 必传字段
- minlength:3 字符串最小长度
- maxlength: 20 字符串最大长度
- min: 2 数值最小为2
- max: 100 数值最大为100
- enum: [‘html’, ‘css’, ‘javascript’, ‘node.js’]
- trim: true 去除字符串两边的空格
- validate: 自定义验证器
- default: 默认值
代码示例
const postSchema = new mongoose.Schema({
title: {
type: String, // 必选字段 required: [true, '请传入文章标题'], // 字符串的最小长度 minlength: [2, '文章长度不能小于2'], // 字符串的最大长度 maxlength: [5, '文章长度最大不能超过5'], // 去除字符串两边的空格 trim: true }, age: {
type: Number, // 数字的最小范围 min: 18, // 数字的最大范围 max: 100 }, publishDate: {
type: Date, // 默认值 default: Date.now }, category: {
type: String, // 枚举 列举出当前字段可以拥有的值 enum: {
values: ['html', 'css', 'javascript', 'node.js'], message: '分类名称要在一定的范围内才可以' } }, author: {
type: String, validate: {
validator: v => {
// 返回布尔值 // true 验证成功 // false 验证失败 // v 要验证的值 return v && v.length > 4 }, // 自定义错误信息 message: '传入的值不符合验证规则' } } });
如何捕获验证的错误信息
讯享网.catch(error => {
// 获取错误信息对象 const err = error.errors; // 循环错误信息对象 for (var attr in err) {
// 将错误信息打印到控制台中 console.log(err[attr]['message']); } })
集合关联(★★)
通常不同集合的数据之间是有关系的,例如文章信息和用户信息存储在不同集合中,但文章是某个用户发表的,要查询文章的所有信息包括发表用户,就需要用到集合关联。
- 使用id对集合进行关联
- 使用populate方法进行关联集合查询
[外链图片转存失败(img-xGEmEFdm-19)(images/关联查询.png)]
示例demo
// 用户集合 const User = mongoose.model('User', new mongoose.Schema({
name: {
type: String } })); // 文章集合 const Post = mongoose.model('Post', new mongoose.Schema({
title: {
type: String }, // 使用ID将文章集合和作者集合进行关联 author: {
type: mongoose.Schema.Types.ObjectId, ref: 'User' } })); const userEntity = mongodb.model('User', user); const postEntity = mongodb.model('Post', title); //1.插入数据的时候需要关联起来 userEntity.create({
name: '王五' }).then(result => console.log(result)); postEntity.create({
title: '射雕英雄传', author: '5c95ac7d18f84d8fac8ee4e1' }).then(resule => console.log(result)); //联合查询 postEntity.find() .populate('author') .then((err, result) => console.log(result));
用户信息增删改查(★★★)
功能
搭建网站服务器,实现客户端与服务器端的通信
连接数据库,创建用户集合,向集合中插入文档
当用户访问/list时,将所有用户信息查询出来
将用户信息和表格HTML进行拼接并将拼接结果响应回客户端
当用户访问/add时,呈现表单页面,并实现添加用户信息功能
当用户访问/modify时,呈现修改页面,并实现修改用户信息功能
当用户访问/delete时,实现用户删除功能
步骤
1.创建服务器
讯享网const http = require('http'); const app = http.createServer();
2.引入mongodb模块,链接数据库
const mongoose = require('mongoose'); // 数据库连接 27017是mongodb数据库的默认端口 mongoose.connect('mongodb://localhost/playground', {
useNewUrlParser: true }) .then(() => console.log('数据库连接成功')) .catch(() => console.log('数据库连接失败'));
3.创建用户表规则,获取集合
讯享网const userSchema = new mongoose.Schema({
name: {
type: String, required: true, minlength: 2, maxlength: 20 }, age: {
type: Number, min: 18, max: 80 }, password: String, email: String, hobbies: [ String ] }); // 创建集合 返回集合构造函数 const User = mongoose.model('User', userSchema);
4.导入用户的数据
mongoimport -d test -c users --file 数据文件的路径
5.当用户访问/list时,将所有用户信息查询出来
5.1 实现路由功能,利用 request对象的method来判断是什么请求,然后拿到请求的path路径,根据不同的path路径来实现不同的功能逻辑,
5.2 如果是list,查询用户信息,然后遍历用户数据,拼接获取的数据,渲染到页面
5.3 如果是add,跳转到添加页面
5.4 如果是modify,跳转到用户修改页面,根据用户id查询出用户的所有信息,显示在页面
5.4 如果是remove,根据用户的id,来删除这条数据,然后重新挑换到list页面
5.5 如果是post请求,路径是add,说明用户点击了提交,需要获取用户信息,然后插入到数据库
5.6 如果是post请求,路径是modify,说明用户点击了修改,需要拿到信息,根据用户id来修改数据库里面的数据
讯享网// 为服务器对象添加请求事件 app.on('request', async (req, res) => {
// 请求方式 const method = req.method; // 请求地址 const {
pathname, query } = url.parse(req.url, true); if (method == 'GET') {
// 呈现用户列表页面 if (pathname == '/list') {
// 查询用户信息 let users = await User.find(); // html字符串,拼接html的内容 let list ='<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用户列表</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <h6> <a href="/add" class="btn btn-primary">添加用户</a> </h6> <table class="table table-striped table-bordered"> <tr> <td>用户名</td> <td>年龄</td> <td>爱好</td> <td>邮箱</td> <td>操作</td> </tr>'; //拿到了用户的信息,用户的信息可能不只一条,所以我们需要去遍历用户信息来拼接里面的内容 users.forEach(item => {
list += ` <tr> <td>${
item.name}</td> <td>${
item.age}</td> <td> `; //一个用户会有多个爱好,所以需要进行遍历 item.hobbies.forEach(item => {
list += `<span>${
item}</span>`; }) list += `</td> <td>${
item.email}</td> <td> <a href="/remove?id=${
item._id}" class="btn btn-danger btn-xs">删除</a> <a href="/modify?id=${
item._id}" class="btn btn-success btn-xs">修改</a> </td> </tr>`; }); //拼接HTML页面剩余的标签 list += ` </table> </div> </body> </html> `; //代码执行到这里,所有的数据已经拼接完毕,需要返回给页面 res.end(list); }else if (pathname == '/add') {
// 呈现添加用户表单页面 let add = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用户列表</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <h3>添加用户</h3> <form method="post" action="/add"> <div class="form-group"> <label>用户名</label> <input name="name" type="text" class="form-control" placeholder="请填写用户名"> </div> <div class="form-group"> <label>密码</label> <input name="password" type="password" class="form-control" placeholder="请输入密码"> </div> <div class="form-group"> <label>年龄</label> <input name="age" type="text" class="form-control" placeholder="请填写邮箱"> </div> <div class="form-group"> <label>邮箱</label> <input name="email" type="email" class="form-control" placeholder="请填写邮箱"> </div> <div class="form-group"> <label>请选择爱好</label> <div> <label class="checkbox-inline"> <input type="checkbox" value="足球" name="hobbies"> 足球 </label> <label class="checkbox-inline"> <input type="checkbox" value="篮球" name="hobbies"> 篮球 </label> <label class="checkbox-inline"> <input type="checkbox" value="橄榄球" name="hobbies"> 橄榄球 </label> <label class="checkbox-inline"> <input type="checkbox" value="敲代码" name="hobbies"> 敲代码 </label> <label class="checkbox-inline"> <input type="checkbox" value="抽烟" name="hobbies"> 抽烟 </label> <label class="checkbox-inline"> <input type="checkbox" value="喝酒" name="hobbies"> 喝酒 </label> <label class="checkbox-inline"> <input type="checkbox" value="烫头" name="hobbies"> 烫头 </label> </div> </div> <button type="submit" class="btn btn-primary">添加用户</button> </form> </div> </body> </html> `; res.end(add); }else if (pathname == '/modify') {
//如果是修改页面,呈现修改的页面,修改肯定是修改某一个用户的信息,需要根据用户的id来进行查询 let user = await User.findOne({
_id: query.id}); let hobbies = ['足球', '篮球', '橄榄球', '敲代码', '抽烟', '喝酒', '烫头', '吃饭', '睡觉', '打豆豆'] console.log(user) // 呈现修改用户表单页面 let modify = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用户列表</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <h3>修改用户</h3> <form method="post" action="/modify?id=${
user._id}"> <div class="form-group"> <label>用户名</label> <input value="${
user.name}" name="name" type="text" class="form-control" placeholder="请填写用户名"> </div> <div class="form-group"> <label>密码</label> <input value="${
user.password}" name="password" type="password" class="form-control" placeholder="请输入密码"> </div> <div class="form-group"> <label>年龄</label> <input value="${
user.age}" name="age" type="text" class="form-control" placeholder="请填写邮箱"> </div> <div class="form-group"> <label>邮箱</label> <input value="${
user.email}" name="email" type="email" class="form-control" placeholder="请填写邮箱"> </div> <div class="form-group"> <label>请选择爱好</label> <div> `; hobbies.forEach(item => {
// 判断当前循环项在不在用户的爱好数据组 let isHobby = user.hobbies.includes(item); if (isHobby) {
modify += ` <label class="checkbox-inline"> <input type="checkbox" value="${
item}" name="hobbies" checked> ${
item} </label> ` }else {
modify += ` <label class="checkbox-inline"> <input type="checkbox" value="${
item}" name="hobbies"> ${
item} </label> ` } }) modify += ` </div> </div> <button type="submit" class="btn btn-primary">修改用户</button> </form> </div> </body> </html> `; res.end(modify); }else if (pathname == '/remove') {
//删除根据用户id进行删除 await User.findOneAndDelete({
_id: query.id}); //通过301的相应码进行重定向 res.writeHead(301, {
Location: '/list' }); res.end(); } }else if (method == 'POST') {
// 用户添加功能 if (pathname == '/add') {
// 接受用户提交的信息 let formData = ''; // 接受post参数,这是一个持续的过程,我们不知道什么时候传递好了,需要在end里面去进行监听 req.on('data', param => {
formData += param; }) // post参数接受完毕 req.on('end', async () => {
//利用 querystring来进行数据的解析 let user = querystring.parse(formData) // 将用户提交的信息添加到数据库中 await User.create(user); // 301代表重定向 // location 跳转地址 res.writeHead(301, {
Location: '/list' }); res.end(); }) }else if (pathname == '/modify') {
// 接受用户提交的信息 let formData = ''; // 接受post参数 req.on('data', param => {
formData += param; }) // post参数接受完毕 req.on('end', async () => {
let user = querystring.parse(formData) // 将用户提交的信息添加到数据库中 await User.updateOne({
_id: query.id}, user); // 301代表重定向 // location 跳转地址 res.writeHead(301, {
Location: '/list' }); res.end(); }) } } }); // 监听端口 app.listen(3000);
相关模块方法
url模块
专门用来处理请求路径相关
url.parse(urlString,boolean,boolean)
parse这个方法可以将一个url的字符串解析并返回一个url的对象
urlString 传入一个url地址的字符串
第二个参数(可省):如果设为 true,则返回的 URL 对象的 query 属性会是一个使用 querystring 模块的 parse() 生成的对象。 如果设为 false,则 query 会是一个未解析未解码的字符串。 默认为 false。
例如:
[外链图片转存失败(img-ciOqEdko-20)(images/url-true.png)]
第三个参数(可省):如果设为 true,则 // 之后至下一个 / 之前的字符串会解析作为 host。 例如, //foo/bar 会解析为 {host: 'foo', pathname: '/bar'} 而不是 {pathname: '//foo/bar'}。 默认为 false。
url.format(urlObj)
format这个方法是将传入的url对象编程一个url字符串并返回
url.format({
protocol:"http:", host:"182.163.0:60", port:"60" }); /* 返回值: 'http://182.163.0:60' */
url.resolve(from, to)
resolve这个方法返回一个格式为"from/to"的字符串,把参数进行一个拼接,然后返回
讯享网const url = require('url'); url.resolve('/one/two/three', 'four'); // '/one/two/four' url.resolve('http://example.com/', '/one'); // 'http://example.com/one' url.resolve('http://example.com/one', '/two'); // 'http://example.com/two'
querystring模块
querystring从字面上的意思就是查询字符串,一般是对http请求所带的数据进行解析
querystring.parse(str,separator,eq,options)
parse这个方法是将一个字符串反序列化为一个对象。
str:指需要反序列化的字符串;
separator(可省):指用于分割str这个字符串的字符或字符串,默认值为"&";
eq(可省):指用于划分键和值的字符或字符串,默认值为"=";
decodeURIComponent:传入一个function,用于对含有%的字符串进行解码,默认值为querystring.unescape,默认是utf-8的编码,如果不是需要进行定义
// 假设 gbkDecodeURIComponent 函数已存在。 querystring.parse('w=%D6%D0%CE%C4&foo=bar', null, null, {
decodeURIComponent: gbkDecodeURIComponent });
querystring.stringify(obj,separator,eq,options)
stringify这个方法是将一个对象序列化成一个字符串,与querystring.parse相对。
obj:指需要序列化的对象
separator(可省):用于连接键值对的字符或字符串,默认值为"&";
eq(可省):用于连接键和值的字符或字符串,默认值为"=";
options(可省):传入一个对象,该对象可设置encodeURIComponent这个属性:
encodeURIComponent:值的类型为function,可以将一个不安全的url字符串转换成百分比的形式,默认值为querystring.escape()。
讯享网querystring.stringify({
name: 'heima', sex: [ 'man', 'women' ] }); /* return: 'name=heima&sex=man&sex=women' */
querystring.escape(str)
escape可使传入的字符串进行编码
querystring.escape("name=黑马程序员"); /* return: 'name%3D%E9%BB%91%E9%A9%AC%E7%A8%8B%E5%BA%8F%E5%91%98' */
querystring.unescape(str)
unescape方法可将含有%的字符串进行解码
讯享网querystring.unescape('name%3D%E9%BB%91%E9%A9%AC%E7%A8%8B%E5%BA%8F%E5%91%98'); /* return: 'name=黑马程序员' */

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/46962.html