iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >搭建node服务(二):操作MySQL
  • 338
分享到

搭建node服务(二):操作MySQL

2024-04-02 19:04:59 338人浏览 八月长安
摘要

搭建node服务(二):操作M

搭建node服务(二):操作Mysql

为了进行复杂信息的存储和查询,服务端系统往往需要数据库操作。数据库分为关系型数据库和非关系型数据库,关系型数据库有mysqloracleSQL Server等,非关系型数据库有Redis(常用来做缓存)、mongoDB等。Mysql是目前很流行的数据库,本文将要介绍如何在node服务中进行MySQL数据库操作。

一、 安装依赖


npm install mysql --save

或者


yarn add mysql

二、建立连接

要想进行数据库操作就需要和数据库建立连接,然后通过连接进行数据库的操作。MySQL的数据库连接方式有以下几种:

  • mysql.createConnection() 每次请求建立一个连接
  • mysql.createPool() 创建连接池,从连接池中获取连接
  • mysql.createPoolCluster() 创建连接池集群,连接池集群可以提供多个主机连接

mysqljs文档中推荐使用第一种方式:每次请求建立一个连接,但是由于频繁的建立、关闭数据库连接,会极大的降低系统的性能,所以我选择了使用连接池的方式,如果对性能有更高的要求,安装了MySQL 集群,可以选择使用连接池集群。

1. 数据库配置

将数据库相关的配置添加到公用的配置文件中,方便项目的初始化。

  • config.js

module.exports = {
    …
    // mysql数据库配置
    mysql: {
        // 主机
        host: 'localhost',
        // 端口
        port: 3306,
        // 用户名
        user: 'root',
        // 密码
        passWord: '123456',
        // 数据库名
        database: 'server-demo',
        // 连接池允许创建的最大连接数,默认值为10
        connectionLimit: 50,
        // 允许挂起的最大连接数,默认值为0,代表挂起的连接数无限制
        queueLimit: 0
    }
};

connectionLimit 和 queueLimit 是数据连接池特有的配置项。

  • connectionLimit 是指连接池允许创建的最大连接数,默认值为10。当获取连接时,如果连接池中有空闲的连接则直接返回一个空闲连接。如果所有连接都被占用,则判断连接池中的连接数是否达到了允许的最大数,如果未达到则创建新的连接,如果已达到则获取连接的请求挂起,等待其他请求完成操作后释放的连接。
  • queueLimit 是指允许挂起的最大连接数,默认值为0,代表挂起的连接数无限制。当连接池中允许创建的所有连接都被占用时,获取连接的请求挂起,等待可用的连接,所有挂起的请求形成一个队列,queueLimit则是指这个队列的最大长度。需要注意的是,当queueLimit为0时并不表示不允许挂起,而是表示对挂起的数目没有限制。

2. 创建连接池

  • db/pool.js


const mysql = require('mysql');
const config = require('../config');
// 创建数据库连接池
const pool = mysql.createPool(config.mysql);
pool.on('acquire', function (connection) {
    console.log(`获取数据库连接 [${connection.threadId}]`);
});
pool.on('connection', function (connection) {
    console.log(`创建数据库连接 [${connection.threadId}]`);
});
pool.on('enqueue', function () {
    console.log('正在等待可用数据库连接');
});
pool.on('release', function (connection) {
    console.log(`数据库连接 [${connection.threadId}] 已释放`);
});
module.exports = pool;

创建数据库连接池pool后,就可以通过pool获取数据库连接了,另外通过监听连接池的事件可以了解连接池中连接的使用情况。
如果将connectionLimit 设为2,queueLimit 设为0,当同时有5个请求获取数据库连接时,线程池的事件日志如下:


正在等待可用数据库连接
正在等待可用数据库连接
正在等待可用数据库连接
创建数据库连接 [1011]
获取数据库连接 [1011]
数据库连接 [1011] 已释放
获取数据库连接 [1011]
创建数据库连接 [1012]
获取数据库连接 [1012]
数据库连接 [1011] 已释放
获取数据库连接 [1011]
数据库连接 [1012] 已释放
获取数据库连接 [1012]
数据库连接 [1011] 已释放
数据库连接 [1012] 已释放

由于线程池允许的最大连接数是2,5个请求中会有2个请求能够得到连接,另外3个请求挂起等待可用连接。由于创建数据库连接的代价比较大,线程池在创建连接时采用懒汉式,也就是,用到时才创建。先得到连接的请求在完成操作后释放连接,放回到连接池,然后挂起的请求从线程池取出空闲的连接进行操作。

三、执行操作

由于mysql 模块的接口都为回调方式的,为了操作方便简单地将接口封装为Promise,相关方法封装如下:


const pool = require('./pool');
// 获取连接
function getConnection () {
    return new Promise((resolve, reject) => {
        pool.getConnection((err, connection) => {
            if (err) {
                console.error('获取数据库连接失败!', err)
                reject(err);
            } else {
                resolve(connection);
            }
        });
    });
}
// 开始数据库事务
function beginTransaction (connection) {
    return new Promise((resolve, reject) => {
        connection.beginTransaction(err => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}
// 提交数据库操作
function commit (connection) {
    return new Promise((resolve, reject) => {
        connection.commit(err => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    })
}
// 回滚数据库操作
function rollback (connection) {
    return new Promise((resolve, reject) => {
        connection.rollback(err => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    })
}

1. 执行普通操作

对于不需要使用事务的普通操作,获取数据库连接connection后,使用connection进行数据库操作,完成后释放连接到连接池,则执行完成一次操作。

  • db/execute.js



async function execute (func, ...params) {
    let connection = null;
    try {
        connection = await getConnection()
        let result = await func(connection, ...params);
        return result
    } finally {
        connection && connection.release && connection.release();
    }
}

2. 执行事务操作

对于很多业务都需要执行事务操作,例如:银行转账,A账户转账给B账户 100元,这个业务操作需要执行两步,从A账户减去100元,然后给B账户增加100元。两个子操作必须全部执行成功才能完成完整的业务操作,如果任意子操作执行失败就需要撤销之前的操作,进行回滚。

对于需要使用事务的操作,获取数据库连接connection后,首先需要调用connection.beginTransaction() 开始事务,然后使用connection进行多步操作,完成后执行connection.commit() 进行提交,则执行完成一次事务操作。如果在执行过程中出现了异常,则执行connection.rollback() 进行回滚操作。

  • db/execute.js


async function executeTransaction(func) {
    const connection = await getConnection();
    await beginTransaction(connection);
    let result = null;
    try {
        result = await func(connection);
        await commit(connection);
        return result
    } catch (err) {
        console.error('事务执行失败,操作回滚');
        await rollback(connection);
        throw err;
    } finally {
        connection && connection.release && connection.release();
    }
}

四、增删改查

增删改查是处理数据的基本原子操作,将这些操作根据操作的特点进行简单的封装。

  • db/curd.js


function query (connection, sql, val) {
    // console.info('sql执行query操作:\n', sql, '\n', val);
    return new Promise((resolve, reject) => {
        connection.query(sql, val, (err, rows) => {
            if (err) {
                console.error('sql执行失败!', sql, '\n', val);
                reject(err);
            } else {
                let results = JSON.parse(JSON.stringify(rows));
                resolve(results);
            }
        });
    });
}

function queryOne (connection, sql, val) {
    return new Promise((resolve, reject) => {
        query(connection, sql, val).then(
            results => {
                let result = results.length > 0 ? results[0] : null;
                resolve(result);
            },
            err => reject(err)
        )
    });
}

function insert (connection, sql, val, skipId) {
    let id = val.id;
    if (!id && !skipId) {
        id = uuid();
        val = {id, ...val};
    }
    return new Promise((resolve, reject) => {
        // console.info('sql执行insert操作:\n', sql, '\n', val);
        connection.query(sql, val, (err, results) => {
            if (err) {
                console.error('sql执行失败!', sql, '\n', val);
                reject(err);
            } else {
                resolve(id);
            }
        });
    });
}

function update (connection, sql, val) {
    // console.info('sql执行update操作:\n', sql, '\n', val);
    return new Promise((resolve, reject) => {
        connection.query(sql, val, (err, results) => {
            if (err) {
                console.error('sql执行失败!', sql, '\n', val);
                reject(err);
            } else {
                resolve(results.affectedRows);
            }
        });
    });
}

function del (connection, sql, val) {
    // console.info('sql执行delete操作:\n', sql, '\n', val);
    return new Promise((resolve, reject) => {
        connection.query(sql, val, (err, results) => {
            if (err) {
                console.error('sql执行失败!', sql, '\n', val);
                reject(err);
            } else {
                // console.log('delete result', results);
                resolve(results.affectedRows);
            }
        });
    });
}

五、代码分层

将代码分层可以降低代码的耦合度,提高可复用性、可维护性,这里将代码分成了3层: Dao层、Service层和Controller层。

  • DAO层:主要负责数据持久化工作;
  • Service层:主要负责业务模块的逻辑设计,此层的业务实现,可以调用DAO层的接口;
  • Controller层:负责具体的业务模块流程的控制,在此层可以调用Service层的接口。

1.DAO层

  • dao/userDao.js

const { query, queryOne, update, insert, del } = require('../db/curd');
class UserDao {
    static async queryUserById (connection, id) {
        const sql = `SELECT user.id, user.account, user.name, user.email, user.phone,
                          user.birthday, user.enable, user.deleteFlag, user.creator,
                          user.createTime, user.updater, user.updateTime
                   FROM sys_user user
                   WHERE user.id = ?`;
        const user = await queryOne(connection, sql, id);
        return user;
    }
    …
}
module.exports = UserDao;

2.Service层

  • service/userService.js
    简单调用一个DAO层方法:

const { execute, executeTransaction } = require('../db/execute');
const UserDao = require('../dao/userDao');
class UserService {
    static async findUserById (id) {
        return await execute(UserDao.queryUserById, id);
}
…
}
module.exports = UserService;

对于复杂些的业务逻辑可以使用匿名函数来实现:


static async findUserWithRoles (id) {
    return await execute (async connection => {
        const user = await UserDao.queryUserById(connection, id);
        if (user) {
            user.roles = await RoleDao.queryRolesByUserId(connection, id);
        }
        return user;
    });
}

如果要执行事务操作,则需要使用executeTransaction 方法:


static async updateUserRoleRelations (userId, roleIds) {
    return await executeTransaction(async connection => {
        const relations = await UserDao.queryUserRoleRelations(connection, userId);
        const oldRoleIds = relations.map(item => item.roleId);
        const newRoleIds = roleIds || [];
        // 新增的角色数组
        const addList = [];
        // 移除的角色数组
        const removeList = [];
        newRoleIds.forEach(roleId => {
            if (oldRoleIds.indexOf(roleId) === -1) {
                addList.push(roleId);
            }
        });
        oldRoleIds.forEach(roleId => {
            if (newRoleIds.indexOf(roleId) === -1) {
                removeList.push(roleId);
            }
        });
        if (addList.length > 0) {
            await UserDao.insertUserRoleRelations(connection, userId, addList);
        }
        if (removeList.length > 0) {
            await UserDao.deleteUserRoleRelations(connection, userId, removeList);
        }
    });
}

3.Controller层

  • controler/userController.js

const UserService = require('../service/userService');
class UserControler {
   static async getUserById (ctx) {
        // 用户ID
        const id = ctx.params.id;
        // 是否包含用户角色信息,如果withRoles 为 "1" 表示需要包含角色信息
        const withRoles = ctx.query.withRoles;
        let user;
        if (withRoles === '1') {
            user = await UserService.findUserWithRoles(id);
        } else {
            user = await UserService.findUserById(id);
        }
        if (user) {
            ctx.body = user;
        } else {
            ctx.body = {
                code: 1004,
                msg: '用户不存在!'
            }
        }
    }
    …
}
module.exports = UserControler;

此示例基于Koa框架,controller 层实现完成后需要添加路由:


const router = new KoaRouter();
const UserController = require('./controler/userControler');
// 获取指定ID的用户
router.get('/users/:id', UserController.getUserById);
// 获取所有用户
router.get('/users', UserControler.getUsers);

对于Koa框架如何使用,这里不再介绍,路由添加完毕后,启动服务,即可使用这些接口,如果本地服务启动的端口为3000,接口请求地址如下:

  • Http://localhost:3000/users/3571a123-0454-49b4-a2bc-8b30a37f0b14
  • http://localhost:3000/users/3571a123-0454-49b4-a2bc-8b30a37f0b14?withRoles=1
  • http://localhost:3000/users/

六、说明

本文介绍了mysql模块的基本使用,对其进行了简单封装,并提供了使用示例。除了使用mysql模块来操作数据库,也可以使用mysql2模块,mysql2的基本用法与mysql一致,另外mysql2还支持Promise,使用起来更方便。本文相关的代码已提交到GitHub以供参考,项目地址: https://github.com/liulinsp/node-server-typeORM-demo。

作者:刘琳

您可能感兴趣的文档:

--结束END--

本文标题: 搭建node服务(二):操作MySQL

本文链接: https://www.lsjlt.com/news/48252.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 搭建node服务(二):操作MySQL
    搭建node服务(二):操作M...
    99+
    2024-04-02
  • xshell搭建mysql的操作步骤
    这篇文章给大家分享的是有关xshell搭建mysql的操作步骤的内容。小编觉得挺实用的,因此分享给大家做个参考。一起跟随小编过来看看吧。xshell搭建mysql的方法:首先进行repo的安装;然后启动my...
    99+
    2024-04-02
  • 原神私服搭建二: 搭建服务端
    原神私服搭建二: 搭建服务端 所需资源: 一:下载jar包 链接:https://github.com/Grasscutters/Grasscutter/releases 二:下载资源 resources(下面两个链接任选其一) 链接一...
    99+
    2023-08-17
    java 开发语言 游戏 mongodb
  • node如何搭建web服务器
    小编给大家分享一下node如何搭建web服务器,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!web服务器的基本知识功能:1.接收...
    99+
    2024-04-02
  • 如何用Node搭建Https服务
    这篇“如何用Node搭建Https服务”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“如何用Node搭建Https服务”文章吧...
    99+
    2023-07-05
  • Node-red操作数据库MySQL
    Node-Red 操作数据库 教程环境: ​ **Mysql8.0 **(本地window) ​ node-red-node-mysql-v1.0.3 ​ Navicat15 ​ Node-red-v...
    99+
    2023-10-24
    mysql 数据库 java
  • 怎么利用Node搭建Https服务
    怎么利用Node搭建Https服务?下面本篇文章给大家介绍一下Node.js搭建Https服务的方法,希望对大家有所帮助!Node.js用于做小程序后台服务,域名要求必须是Https协议。在Node.js开启Http服务是非常简单的,如下:...
    99+
    2023-05-14
    Https服务 nodejs​ node
  • Python操作MySQL(二) ORM
    SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。 一、安装 pip3 install ...
    99+
    2023-01-31
    操作 Python ORM
  • MySQL 搭建主从同步实现操作
    目录一、MySQL 8.0 主从同步二、MySQL主从搭建2.1 Master 上的操作2.2 Slave 上的操作一、MySQL 8.0 主从同步 主从同步的流程(原理): mas...
    99+
    2024-04-02
  • node如何操作mysql数据库
    这篇文章主要介绍了node如何操作mysql数据库,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。具体如下:1、建立数据库连接:createC...
    99+
    2024-04-02
  • Node项目中如何操作MySQL
    这篇文章主要介绍“Node项目中如何操作MySQL”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node项目中如何操作MySQL”文章能帮助大家解决问题。数据库数据库(database)是用来组织、...
    99+
    2023-07-05
  • 电脑搭建云服务器怎么操作
    首先,需要下载好 Chrome 浏览器,并安装好 Chrome 扩展程序。这个过程需要注意:Chrome 安装程序下载地址会根据网络情况和实际使用环境而有所差异,所以需要根据实际情况自行下载相应版本的 Chrome 安装程序,以确保能够成...
    99+
    2023-10-27
    操作 服务器 电脑
  • Node搭建https服务器实例详解
     本文旨在分享搭建https服务器的过程,具体知识点以及相关概念请自行查询。 第一步:创建文件目录如下,在index中引用外部的script.js文件,server.js是...
    99+
    2023-05-18
    Node 搭建 https 服务器
  • 电脑搭建云服务器怎么操作的
    确定云服务器的类型:目前常见的云服务器有公有云和私有云两种类型。 选择云服务器提供商:可以选择知名的云服务器提供商,比如AWS、阿里云、UCloud等。 配置云服务器:在云服务器提供商的网站上配置云服务器的相关参数,包括服务器的IP地址、...
    99+
    2023-10-27
    操作 服务器 电脑
  • 搭建自己的云服务器怎么操作
    搭建自己的云服务器的操作很简单,以下是一些基本步骤: 选择云服务器提供商:选择一个安全可靠的云服务器提供商,可以提供云主机或负载均衡等云服务,例如Amazon、Google或微软等。 创建网站:首先,您需要选择一个网站程序,并将其导入云...
    99+
    2023-10-26
    自己的 操作 服务器
  • 使用亚马逊云搭建node服务器
    要搭建一个亚马逊云的Node服务器,您需要了解一些基本的配置选项。您可以使用AWS提供的命令行界面或者使用Microsoft Azure提供的命令行界面来完成此操作。 以下是一个简单的搭建Node服务器的步骤: 安装AWS Kubern...
    99+
    2023-10-27
    亚马逊 服务器 node
  • MySQL如何搭建主从同步实现操作
    小编给大家分享一下MySQL如何搭建主从同步实现操作,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、MySQL 8.0 主从同步主从同步的流程(原理):mast...
    99+
    2023-06-29
  • 搭建自己的云服务器怎么操作的
    搭建自己的云服务器是一个简单而有趣的过程,但是需要注意以下几个方面: 1.选择云服务器供应商:在选择云服务器供应商时,要了解他们的支持和服务情况,如数据中心的位置、服务器数量、存储容量等。可以通过搜索引擎、官网、第三方论坛等途径了解更多信...
    99+
    2023-10-26
    自己的 操作 服务器
  • 电脑搭建云服务器怎么操作手机
    首先,云服务器是基于虚拟化技术搭建的服务器,它利用虚拟化技术将服务器分成多个虚拟机,每个虚拟机都可以运行自己的应用程序。在这种方式下,用户不需要购买或租用物理服务器,只需购买一个云服务器即可。 接下来,让我们了解一下如何操作手机。首先,用...
    99+
    2023-10-27
    操作 服务器 电脑
  • 阿里云服务器搭建hive开发操作界面
    简介 阿里云服务器是阿里云计算服务提供的一种弹性计算能力,可以满足不同规模和需求的应用场景。本文将介绍如何在阿里云服务器上搭建hive开发操作界面,以便进行数据处理和分析。详细说明1. 安装Java环境首先,我们需要在阿里云服务器上安装Ja...
    99+
    2024-01-17
    阿里 操作界面 服务器
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作