基于node.js的express框架的图书管理功能(2)

  之前的图书管理功能的数据是存放在json文件中的,通过读取json文件的内容渲染到页面上,每次读取都要遍历整个文件,当数据量大时很不方便,把数据存放在数据库中才是正确的做法。

1.操作数据库的基本功能

在Mysql中新建一个数据库book,新建一张book的表用来存放图书的数据信息,将id值设为自增。

利用数据库自增功能有一个问题:在执行删除操作后,再添加数据时,id会出现间隔现象,如下图:

数据库搭建好后,创建一个项目测试一下数据库操作的一些基本功能:

新建一个文件夹mydb

准备一个入口文件:index.js

初始化该项目:npm init -y

安装需要的依赖的包:

npm install mysqljs/mysql

①插入数据

/**
 * 插入数据
 */
//加载数据库驱动
const mysql = require('mysql');
//创建数据库链接
const connection = mysql.createConnection({
    host: 'localhost',   //数据库所在的服务器的域名或者IP地址
    user: 'root',      //登录数据库的账号
    password: 'password',
    database: 'book'    //数据库的名称book,注意这里不是链接的名称,我创建的链接的名称为mybook
});
//执行连接操作
connection.connect();
//使用mysql第三方包简化了insert操作,?用来填充数据,只需要提供一个对象
let sql = 'insert into book set ?'
//用来插入的数据
let data = {
    name: '明朝那些事',
    author: '当年明月',
    category: '文学',
    description: '明朝的历史'
}

//操作数据库
connection.query(sql,data,(err,results,fields) => {
    if(err) return;
    //console.log(results);   
    if(results.affectedRows == 1){
        console.log('数据插入成功');
    }
});
//关闭数据库
connection.end();

②更新数据

/**
 * 更新数据
 */
//加载数据库驱动
const mysql = require('mysql');
//创建数据库链接
const connection = mysql.createConnection({
    host: 'localhost',   //数据库所在的服务器的域名或者IP地址
    user: 'root',      //登录数据库的账号
    password: 'password',
    database: 'book'    //数据库的名称book,注意这里不是链接的名称,我创建的链接的名称为mybook
});
//执行连接操作
connection.connect();

let sql = 'update book set name=?,author=?,category=?,description=? where id=?'
//用来更新的数据
let data = ['浪潮之巅','吴军','计算机','IT巨头的兴衰史',6];

//操作数据库
connection.query(sql,data,(err,results,fields) => {
    if(err) return;
    //console.log(results);   
    if(results.affectedRows == 1){
        console.log('数据更新成功');
    }
});
//关闭数据库
connection.end();

③删除数据

/**
 * 删除数据
 */
//加载数据库驱动
const mysql = require('mysql');
//创建数据库链接
const connection = mysql.createConnection({
    host: 'localhost',   //数据库所在的服务器的域名或者IP地址
    user: 'root',      //登录数据库的账号
    password: 'password',
    database: 'book'    //数据库的名称book,注意这里不是链接的名称,我创建的链接的名称为mybook
});
//执行连接操作
connection.connect();

let sql = 'delete from book where id=?'
//用来删除的数据
let data = [6];

//操作数据库
connection.query(sql,data,(err,results,fields) => {
    if(err) return;
    //console.log(results);   
    if(results.affectedRows == 1){
        console.log('数据删除成功');
    }
});
//关闭数据库
connection.end();

④查询数据

/**
 * 操作数据库基本步骤
 */
//加载数据库驱动
const mysql = require('mysql');
//创建数据库链接
const connection = mysql.createConnection({
    host: 'localhost',   //数据库所在的服务器的域名或者IP地址
    user: 'root',      //登录数据库的账号
    password: 'password',
    database: 'book'    //数据库的名称book,注意这里不是链接的名称,我创建的链接的名称为mybook
});
//执行连接操作
connection.connect();

let sql = 'select * from book'
let data = null;

//操作数据库
connection.query('select 1+1 as solution',(err,results,fields) => {
    if(err) return;
    console.log(results[0]);    
});
//关闭数据库
connection.end();

 上述的增删改查的代码有很多重复性,可以将以上功能封装为一个通用的api文件db.js:

/**
 * 封装操作数据库的通用API
 */
const mysql = require('mysql');

exports.base = (sql,data,callback) => {
    //创建数据库链接
    const connection = mysql.createConnection({
        host: 'localhost',   //数据库所在的服务器的域名或者IP地址
        user: 'root',      //登录数据库的账号
        password: 'password',
        database: 'book'    //数据库的名称book,注意这里不是链接的名称,我创建的链接的名称为mybook
    });
    //执行连接操作
    connection.connect();
    
    //操作数据库(数据库操作也是异步的,异步不同通过返回值来处理,只能通过回调函数)
    connection.query(sql,data,(err,results,fields) => {
        if(err) throw err;
        callback(results);
    });
    //关闭数据库
    connection.end();
}

测试该api:

/**
 * 测试通用API
 */
const db = require('./db.js');

//插入操作
let sql = 'insert into book set ?';
let data = {
    name: '笑傲江湖',
    author: '金庸',
    category: '文学',
    description: '武侠小说'
};
db.base(sql,data,(result) => {
    console.log(result);
});

//更新操作
let sql = 'update book set name=?,author=?,category=?,description=? where id=?';
let data = ['天龙八部','金庸','文学','武侠小说',5];
db.base(sql,data,(result) => {
    console.log(result);
});

//删除操作
let sql = 'delete from book where id=?'
let data = [7];
db.base(sql,data,(result) => {
    console.log(result);
});

//查询操作
let sql = 'select * from book where id = ?';
let data = [8];
db.base(sql,data,(result) => {
    console.log(result);
});

2.通过一个小的登陆验证功能来测试前后端+数据库一起的功能:

后台逻辑代码login.js:

/**
 * 登陆验证(前端+后端+数据库)
 */
const express = require('express');
const bodyParser = require('body-parser');
const db = require('./db.js');
const app = express();
//启动bodyParser这个API来处理参数,处理表单提交的
app.use(bodyParser.urlencoded({extended:false}));
app.use(express.static('./public'));

app.post('/check',(req,res) => {
    let param = req.body;
    console.log(param);
    let sql = 'select count(*) as total from user where username=? and password=?';
    let data = [param.username,param.password];

    db.base(sql,data,(result) => {
        console.log(result);
        if(result[0].total == 1){
            res.send('login success!');
        }else{
            res.send('login failure!');
        }
    });
});

app.listen(3000,() => {
    console.log('running...');
});

前端页面代码login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>登录</title>
</head>
<body>
    <form action="http://localhost:3000/check" method="POST">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit" value="登录">
    </form>
</body>
</html>

3.重构图书管理功能,使数据从数据库中获取

将操作数据库的通用api文件db.js拷贝到mybook项目下,还需要安装mysql的api

注意:由于desc字段在数据库中是关键字,不能使用,所以把该字段名称改为description,之前的.art文件里用到的desc字段名称全部要改过来,否则执行过程中会报错

 

 

 只需修改service.js业务模块中的功能:

/**
 * 业务模块
 */
const data = require('./data.json');
const path = require('path');
const fs = require('fs');
const db = require('./db.js');

/* //自动生成图书编号(自增)
let maxBookCode = () => {
    let arr = [];
    data.forEach(item => {
        arr.push(item.id);
    });
    return Math.max.apply(null,arr);
}

//把内存数据写入到文件
let writeDataFile = (res) => {
    //需要把内存中的数据写入文件
    //JSON.stringify(data)仅传data一个参数的话,data.json文件是压缩形式的
    fs.writeFile(path.join(__dirname,'data.json'),JSON.stringify(data,null,4),(err) => {
        if(err){
            res.send('server err');  
        }
        //文件写入成功之后重新跳转到主页面
        res.redirect('/');
    });
}
 */
 //渲染主页面
 exports.showIndex = (req,res) => {
     let sql = 'select * from book';
    //从数据库中获取内容
    db.base(sql,null,(result) => {
        //数据库获取的内容只能通过回调函数来得到
        res.render('index',{list:result});
    })
    //res.render('index',{list:data});
 }

 //跳转到添加图书的页面
 exports.toAddBook = (req,res) => {
     //render将会根据views中的模板文件进行渲染,渲染的是空对象{}
     res.render('addBook',{});
 }

 //添加图书保存数据
 exports.addBook = (req,res) => {
     //获取表单数据
     let info = req.body;
     let book = {};
     for (const key in info) {
         book[key] = info[key];
     }

     let sql = 'insert into book set ?'
     db.base(sql,book,(result) => {
         if(result.affectedRows == 1){
             //添加成功后跳转到主页面
             res.redirect('/');
         }
     });

     /* book.id = maxBookCode()+1;
     data.push(book);
     //需要把内存中的数据写入文件
     writeDataFile(res); */
 }

 //跳转到编辑页面
 exports.toEditBook = (req,res) => {
    let id = req.query.id;
    let sql = 'select * from book where id=?';
    let data = [id];
    db.base(sql,data,(result)=>{
        //注意:必须使用result[0]获取数据才能渲染到页面上,因为得到的resul结果集是一个数组,不是对象,渲染不了
        res.render('editBook',result[0]);
    });
    /* let book = null;
    data.forEach((item)=>{
        if(id == item.id){
            book = item;
            //break;
            //forEach循环中不能有break,用return终结即可
            return;
        }
    }); 
    //render将会根据views中的模板文件进行渲染,渲染的是对应图书的完整信息
    res.render('editBook',book); */
 }

 //编辑图书更新数据:
 //1.先查询出对应的数据并渲染到页面上
 //2.然后再提交表单,再重新保存,写入文件
 //编辑的时候要告诉服务器编辑的是哪条数据
 exports.editBook = (req,res) => {
    //报错:为什么总是跳转到addBook的页面?  问题已解决:editBook.art页面的action路径错写成'/addBook',改成'/editBook'就可以正常运行
    //获取表单的数据
    let info = req.body;

    let sql = 'update book set name=?,author=?,category=?,description=? where id=?';
    let data = [info.name,info.author,info.category,info.description,info.id];
    db.base(sql,data,(result) => {
        if(result.affectedRows == 1){
            //更新成功后跳转到主页面
            res.redirect('/');
        }
        else{
            res.send('edit failure');
        }
    });

     /* //覆盖原有的数据
     data.forEach((item)=>{
         if(info.id == item.id){
             for (const key in info) {
                 item[key] = info[key];
             }
             return;
         }
     });
     //需要把内存中的数据写入文件
     writeDataFile(res); */
 }

 //删除图书信息
 exports.deleteBook = (req,res) => {
     //先获取到传过来的id
     let id = req.query.id;

    let sql = 'delete from book where id=?';
    let data = [id];
    db.base(sql,data,(result) => {
        if(result.affectedRows == 1){
            //删除成功后跳转到主页面
            res.redirect('/');
        }
    });

    /*  data.forEach((item,index)=>{
         if(item.id == id){
            //删除数组的一项数据
            data.splice(index,1);  
         }
         return;
     });
     //需要把内存中的数据写入文件
     writeDataFile(res); */
 }
原文地址:https://www.cnblogs.com/zcy9838/p/11639293.html