nodejs数据库项目的搭建
假设我们现在要搭建上面的管理系统项目
- 先通过思维导图划分自己的模块与功能
- 确定数据库的字段,再创建数据库
- 通过nodejs搭建
一般情况下的数据库都会涉及到表的操作,一个模块可能就是一个表,上面有三个模块,在数据库里面可能就是三个表的操作
但是无论是哪一张表的操作都离不开四个操作“增,删,改,查”。这个时候针对四种操作,我们项目应该如何搭建
初始化项目
新建文件夹rental_house
,然后打开文件夹,调出命令行,输入如下命令
$ npm init --yes
形成package.json
{
"name": "rental_house",
"version": "1.0.0",
"description": "房屋出租系统 ",
"main": "app.js",
"scripts": {
},
"keywords": [
"node.js", "房屋出租"
],
"author": "杨标",
"license": "MIT"
}
安装依赖包
因为当前只需要完成数据库操作的功能,所以我们只用安装mysql
的包即可
$ npm install mysql --save
导入数据库操作的封装对象
之前的时候我们已经封装了一个文件叫DBUtils.js
的文件,将这个文件放在utils
的目录
utils的目录是用于放第三方工具类的,可以放自己的封装的工具,也可以放其它地方获取的工具类方法
编写服务层
服务层就是用于操作数据库的,为前面的程序提供的服务的
前面的程序指的是Controller,也可能是API等一系列操作
服务层其实就是一个一个对象,这个对象用于操作数据库,这个对象里面编写了很多常用的数据库的操作方法,如新增,修改,删除,查询,分页查询 等一系列方法
创建service的文件夹
根据上面的图我们可以得到一点,我们的service
应该不只一个文件, 所以我们新建一个文件夹
根据数据库创建service对象
因为service主要是提供服务,操作数据表,所以正常情况下应该是一个数据表对应一个service
文件。现在有三个数据表,所以我们构建三个数据库文件
admin_info
管理员表room_info
房间表stu_info
学生表
表名 | 对象名 | 备注 |
---|---|---|
admin_info | AdminInfoService.js | |
room_info | RoomInfoService.js | |
stu_info | StuInfoService.js |
完成功能里面具体的操作
假设当前项目经理分配给我的是room_info
房间管理的模块,所以我现在只用专注于RoomInfoService.js
这个即可
当我们拿到上面的原型图,我们的servic肯定要为这一个界面提供服务,那么RoomInfoServcie
里面应该如何编写代码呢
/**
* @description RoomInfoServcie 操作room_info数据表
*/
class RoomInfoServcie {
add() {
}
deleteById() {
}
update() {
}
query() {
}
findById() {
}
}
module.exports = RoomInfoServcie;
现在强制约束,每个service
至少是有这5个方法
我们在中间添加了一层BaseService
,以期达到项目模块的低耦合的操作
DBUtils.js文件
/**
* @description 专门用于操作数据库的文件
* @author 杨标
* @version 1.0
*/
const mysql = require("mysql");
class DBUtils {
/**
* @description 获取数据库连接
* @returns {mysql.Connection}
*/
getConn() {
let conn = mysql.createConnection({
host: "192.168.1.254",
port: 3306,
user: "dev_h2103",
password: "123456",
database: "rental_house"
});
return conn;
}
/**
* @description 执行一条SQL语句返回结果
* @param {string} strSql 要执行的SQL语句
* @param {Array} params 执行SQL语句所需要的参数
* @returns {Promise<Array>|Promise<mysql.OkPacket>} 返回数据库执行的承诺
*/
executeSql(strSql, params = []) {
let p = new Promise((resolve, reject) => {
let conn = this.getConn();
conn.connect();
conn.query(strSql, params, (error, results) => {
if (error) {
reject(error);
}
else {
resolve(results);
}
conn.end();
});
});
return p;
}
}
module.exports = DBUtils;
BaseService.js文件
/**
* @description 公共的基础服务类,主要用于解耦,并提供基础的方法
*/
const DBUtils = require("../utils/DBUtils.js");
class BaseService extends DBUtils {
}
module.exports = BaseService;
RoomInfoService.js文件
/**
* @description RoomInfoServcie 操作room_info数据表
*/
const BaseService = require("./BaseService.js");
class RoomInfoServcie extends BaseService {
/**
* @description 新增房间信息
* @param {{room_name, max_count, kt, networknetwork, washroom, room_size}} param
* @returns {Promise<import("mysql").OkPacket>} 返回数据库操作的结果
*/
add({ room_name, max_count, kt, network, washroom, room_size }) {
let strSql = `insert into room_info (room_name,max_count,kt,network,washroom,room_size) values (?,?,?,?,?,?) `;
return this.executeSql(strSql, [room_name, max_count, kt, network, washroom, room_size]);
}
/**
* @description 根据id删除房间信息
* @param {number} id 要删除项的主键id
* @returns {Promise<boolean>} true代表删除成功,false代表删了作制作
*/
async deleteById(id) {
let strSql = ` delete from room_info where id = ? `;
let results = await this.executeSql(strSql, [id]);
return results.affectedRows > 0 ? true : false;
}
/**
* @description 修改房间信息
* @param {{ id, room_name, max_count, kt, network, washroom, room_size }} param 修改的对象
* @returns {Promise<boolean>} true代表修改成功,false代表修改失败
*/
async update({ id, room_name, max_count, kt, network, washroom, room_size }) {
let strSql = ` UPDATE room_info SET room_name=?, max_count=?, kt=?, network=?, washroom=?, room_size=? WHERE id=?; `;
let results = await this.executeSql(strSql, [room_name, max_count, kt, network, washroom, room_size, id]);
return results.affectedRows > 0 ? true : false;
}
/**
* @description 查询房间信息
* @param {{ room_name, kt, network, washroom }} param 查询条件
* @returns {Promise<Array>} 返回数据库执行结果
*/
query({ room_name, kt, network, washroom }) {
let strSql = ` select * from room_info where 1 `;
let ps = [];
if (room_name) {
strSql += ` and room_name like ? `;
ps.push(`%${room_name}%`);
}
if (kt) {
strSql += ` and kt = ? `;
ps.push(kt);
}
if (network) {
strSql += ` and network = ? `;
ps.push(network);
}
if (washroom) {
strSql += ` and washroom = ? `;
ps.push(washroom);
}
return this.executeSql(strSql, ps);
}
/**
* @description 根据一个id得到一条数据
* @param {number} id 主键id
* @returns {Promise<object>} 返回数据库查询结果
*/
async findById(id) {
let strSql = ` select * from room_info where id = ? `;
let results = await this.executeSql(strSql, [id]);
return results[0];
}
/**
* @description 获取所有数据
* @returns {Promise<[]>} 返回数据库查询结果
*/
getAllList() {
let strSql = ` select * from room_info `;
return this.executeSql(strSql);
}
}
module.exports = RoomInfoServcie;
BaseService的功能重构
/**
* @description 公共的基础服务类,主要用于解耦,并提供基础的方法
*/
const DBUtils = require("../utils/DBUtils.js");
class BaseService extends DBUtils {
constructor() {
super();
this.tableMap = {
admin_info: "admin_info",
room_info: "room_info",
stu_info: "stu_info"
};
this.currentTableName = "";
}
/**
* @description 根据一个主键去删除
* @param {number} id
* @returns {Promise<boolean>} true代表删除成功,false代表删除失败
*/
async deleteById(id) {
let strSql = ` delete from ${this.currentTableName} where id = ? `;
let results = await this.executeSql(strSql, [id]);
return results.affectedRows > 0 ? true : false;
}
/**
* @description 根据主键id查询信息
* @param {number} id 查询的主键id
* @returns {Promise<object>} 数据库查询的结果
*/
async findById(id) {
let strSql = ` select * from ${this.currentTableName} where id = ? `;
let results = await this.executeSql(strSql, [id]);
return results[0];
}
/**
* @description 查询所有数据
* @returns {Promise<[]>} 返回数据库查询的结果
*/
getAllList() {
let strSql = ` select * from ${this.currentTableName} `;
return this.executeSql(strSql);
}
}
module.exports = BaseService;
在上面的代码里面,为了更好的实现与数据库的解耦关系,我们在当前的BaseService
里面创建了一个tableMap
对象,用于映射数据库的表,同时提取出了3个公共的方法
StuInfoService.js
/**
* @description StuInfoServcie 操作stu_info数据表
*/
const BaseService = require("./BaseService.js");
class StuInfoService extends BaseService {
constructor() {
super();
this.currentTableName = this.tableMap.stu_info;
}
}
module.exports = StuInfoService;
简单工厂模式
我们后期在测试这些功能(或者使用这些service)的时候我们又发现一个问题,如果当前的某一个文件同时使用了多个service
这个每个文件都需要导入一次,很麻烦
/**
* 测试基础的公共方法
*/
const AdminInfoService = require("../services/AdminInfoService.js");
const RoomInfoService = require("../services/RoomInfoService.js");
const StuInfoService = require("../services/StuInfoService.js");
//每个文件都要导这么多的service,怎么办呢
同时导入的文件每次都要new
一下才能使用,这个时候就很有问题
现象我为了实现与项目前端面结合,我们需要降低这个耦合性,所以我们会生产一个对象叫工厂对象,这个工厂专门用于生成所有的Service
,最后只要让这一个文件与所有的页面实现耦合就可以了
ServiceFactory.js文件
/**
* 工厂模式
*/
class ServiceFactory {
constructor(){
throw new Error("当前对象不需要new");
}
static createAdminInfoService() {
let AdminInfoService = require("../services/AdminInfoService.js");
return new AdminInfoService();
}
static createRoomInfoService() {
let RoomInfoService = require("../services/RoomInfoService.js")
return new RoomInfoService();
}
static createStuInfoService() {
let StuInfoService = require("../services/StuInfoService.js")
return new StuInfoService();
}
}
module.exports = ServiceFactory;
上面是我们手动创建的一个工厂,这样后期我们要使用Service统一使用这个文件就可以了
当我们再去使用这个Service的时候就可以像这面这种试使用了
/**
* 测试基础的公共方法
*/
const ServiceFactory = require("../factory/ServiceFactory.js");
//测试admin_info 获取所有数据
/*
const getAdminInfoAllData = async () => {
let results = await ServiceFactory.createAdminInfoService().getAllList();
console.log(results);
}
const getRoomInfo = async () => {
let results = await ServiceFactory.createRoomInfoService().findById(2);
console.log(results);
}
getRoomInfo();
*/
ServiceFactory.createAdminInfoService().getAllList().then(results => console.log(results));
ServiceFactory.createRoomInfoService().findById(2).then(results => console.log(results));
这个时候我们可以看到所有的Service都是由当前的工厂ServiceFactory
产生
ServcieFactory
里面所有的方法都是静态方法,不需要new就可以使用
之前的工厂模式代码里面,我们使用了class
的静态方法,现在还有一种情况可以使用下面的代码【标哥推荐】
/**
* 工厂模式
*/
const ServiceFactory = Object.freeze({
get adminInfoService() {
let AdminInfoService = require("../services/AdminInfoService.js");
return new AdminInfoService();
},
get roomInfoService() {
let RoomInfoService = require("../services/RoomInfoService.js")
return new RoomInfoService();
},
get stuInfoService() {
let StuInfoService = require("../services/StuInfoService.js")
return new StuInfoService();
}
});
module.exports = ServiceFactory;
/**
* 1.访问器属性get
* 2.使用对象的冻结来保护对象的内部
*/
抽象工厂模式
/**
* 抽象工厂模式
*/
const path = require("path");
const fs = require("fs");
/**
* @typedef ServiceFactoryType
* @property {import("../services/AdminInfoService.js")} adminInfoService
* @property {import("../services/RoomInfoService.js")} roomInfoService
* @property {import("../services/StuInfoService.js")} stuInfoService
*/
/**
* @type {ServiceFactoryType}
*/
const ServiceFactory = (() => {
let obj = {}
let arr = fs.readdirSync(path.join(__dirname, "../services"));
for (let item of arr) {
//1.1构建属性名
let propertyName = item.replace(".js", "").replace(/^[A-Z]/, p => p.toLowerCase());
//1.2导入文件,反射对象
//1.3判断导入进来的文件否是一个构造函数
let temp = require(path.join(__dirname, "../services", item));
if (typeof temp === "function") {
obj[propertyName] = Reflect.construct(temp, []);
}
}
return obj;
})();
module.exports = ServiceFactory;
在当前的抽象工厂模式里面,我们直接读取了
services
文件夹的路径,然后找到文件 ,动态创建对象,动态导入文件,然后反射这个构造函数得到对象后期如果有新的数据表,我们只需要在
services
文件夹下面创建新的XXXService.js文件就可以了,系统会自动的帮我们反射出来
评论区