Nodejs подключается к Mysql

Node.js
Nodejs подключается к Mysql

В этой статье мы будем использовать Nodejs для подключения к Mysql и реализации основных операций CRUD.

Основные технические моменты следующего примера кода включают

  • базовая структура
    • Koa
    • Koa-router
    • koa-nunjucks-2
  • Пакет подключения MySQL
    • mysqljs

Студенты, не знакомые с Mysql, могут обратиться к этой статье.Поиграйте с mysql на фронтэнде

0. Предпосылки

  • Установите базу данных mysql и запустите
  • Установите Nodejs (это не должно быть проблемой)

1. Узел подключается к базе данных

  • Создать пустую папку
  • воплощать в жизньyarn add koa koa-router mysql
  • Создайте файл js (test.js) в корневом каталоге, чтобы проверить подключение к операции с базой данных.
  • Сначала пишем кусок кода в test.js, выводим hello, чтобы программа запуска не сообщала об ошибках
    const Koa = require("koa") // 导入koa
    const Router = require("koa-router") //导入koa-router
    const mysql = require("mysql")  // 导入mysql,连接mysql 需要用到
    const app = new Koa(); // 实例化koa
    const router = new Router(); // 实例化路由
    // 创建一个路径为/hello的get请求
    router.get("/hello", async ctx => {
    // 返回 字符串 hello
        ctx.body = "hello"
    
    })
    
    // koa注册路由相关
    app
    .use(router.routes())
    .use(router.allowedMethods())
    // 监听端口
    .listen(3333,()=>{
        console.log("server running port:" + 3333);
    })
    
    • Выполнить в корневом каталоге проектаnode test.jsилиnodemon test.jsСтартовый проект
    • использоватьnodemonДля запуска проекта требуется глобальная установкаyarn global add nodemonилиnpm i -g nodemon
    • использоватьnodemonСтартовый проект,nodemonБудет следить за файлами в каталоге запуска, и если какой-либо файл изменится,nodemonавтоматически перезапустит приложение узла, настоятельно рекомендуетсяnodemonзапустить проект узла
    • После запуска проекта входим в браузерhttp://localhost:3333/hello, вы можете увидеть выходной текст hello на странице
      mysql_hello.png
    • После появления этого интерфейса это доказывает, что с запуском нашего проекта проблем нет.
    • Далее мы используем узел для подключения к базе данных mysql.
  • Сначала мы готовим волну данных
          CREATE DATABASE db1;
          USE db1;
          CREATE TABLE user (
        	id INT PRIMARY KEY auto_increment,
          	NAME VARCHAR(20) NOT NULL,
         	age INT NOT NULL
          ); 
          INSERT INTO user VALUES 
          (null, "张三", 23),
          (null, "李四", 24),
          (null, "王五", 25),
          (null, "赵六", 26);
    

2. Подключитесь к базе данных mysql, чтобы реализовать функцию отображения таблицы.

  • Далее пишем код для подключения к mysql в test.js

      const Koa = require("koa") // 导入koa
      const Router = require("koa-router") //导入koa-router
      const mysql = require("mysql")  // 导入mysql,连接mysql 需要用到
      const app = new Koa(); // 实例化koa
      const router = new Router(); -- 实例化路由
    
      // mysqljs 连接 mysql数据库
      let connection = mysql.createConnection({
          host: '127.0.0.1', // mysql所在的主机,本地的话就是 127.0.0.1 或者 localhost, 如果数据库在服务器上,就写服务器的ip
          user: 'root', // mysql的用户名
          password: '密码', // mysql的密码
          database: 'db1' // 你要连接那个数据库
      })
    
      // 连接 mysql
      connection.connect(err=>{
          // err代表失败
          if(err) {
              console.log("数据库初始化失败");
          }else {
              console.log("数据库初始化成功");
          }
      })
    
      // 创建一个路径为/hello的get请求
      router.get("/hello", async ctx => {
      // 返回 字符串 hello
          ctx.body = "hello"
    
      })
    
      // koa注册路由相关
      app
      .use(router.routes())
      .use(router.allowedMethods())
      // 监听端口
      .listen(3333,()=>{
          console.log("server running port:" + 3333);
      })
      
    

    data_mk.png mysql_connect_success.png

    • когда терминал выдает数据库初始化成功Текст означает, что подключение к базе данных прошло успешно.
    • Только что мы подготовили четыре фрагмента данных в базе данных db1, затем мы можем запросить данные и отобразить их на консоли.
  • Мы добавляем этот код запроса в метод connection.connect

    • Первый параметр метода connection.query — это строка типаsqlОператор, второй параметр является необязательным, как будет сказано далее, последний является методом, который содержит информацию об ошибке и данные о правильном ответе

        const selectSql = "SELECT * FROM user"
        connection.query(selectSql, (err,res) => {
            if(err) console.log(err);
            console.log(res);
        })
      
  • Возвращаемые данные такиеmysql_select_log.png

    • На данный момент данные в базе данных были запрошены, после чего мы можем вернуть данные во внешний интерфейс в формате JSON.
  • Верните данные в браузер в формате JSON, добавив этот код

      // 因为 mysqljs不支持 Promise方式CRUD数据
      // 所以我们做一个简单的封装
      function resDb(sql) {
          return new Promise((resolve,reject) => {
              connection.query(sql, (err,res) => {
                  if(err) {
                      reject(err)
                  }else {
                      resolve(res)
                  }
              })
          })
      }
    
      //请求 /userAll 的时候返回数据
      router.get("/userAll", async ctx => {
          ctx.body =  await resDb("SELECT * FROM user")
      })
    

    select_mysql_userAll.png

    • Вот эти данные то что нам нужно, ээээээ, данные возвращаются, делаем фронтенд, как тут не быть страницы, сначала добавить табличную страницу для отображения данных, вотnunjucksДвижок шаблонов, давайте сначала установим егоyarn add koa-nunjucks-2
  • Добавьте этот код в test.js

      const koaNunjucks = require('koa-nunjucks-2');
      const path = require('path');
    
       // 注入 nunjucks 模板引擎
       app.use(koaNunjucks({
          ext: 'html', // html文件的后缀名
          path: path.join(__dirname, 'views'), // 视图文件放在哪个文件夹下
          nunjucksConfig: {
            trimBlocks: true // 自动去除 block/tag 后面的换行符
          }
        }));
        //在 /userAll这个路由中我们不直接返回数据了,我们返回table.html页面
      router.get("/userAll", async ctx => {
      const userAll = await resDb("SELECT * FROM user")
      await ctx.render("table",{userAll})
      })
    
  • Через механизм шаблонов nunjucks мы помещаем все файлы html в папку представлений корневого каталога, затем нам нужно создать папку представлений в корневом каталоге и создать в папке файл table.html, код файла такой следует

        <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>Document</title>
              <style>
                  .table{
                      width: 500px;
                  }
                  td{
                      text-align: center;
                  }
              </style>
          </head>
          <body>
              <table class="table" border="1"  cellspacing="0">
                  <thead>
                      <tr>
                          <th>id</th>
                          <th>姓名</th>
                          <th>年龄</th>
                      </tr>
                  </thead>
                  <tbody>
                      {% for user in userAll %}
                      <tr >
                          <td>{{user.id}}</td>
                          <td>{{user.NAME}}</td>
                          <td>{{user.age}}</td>
                      </tr>
                      {% endfor %}
                  </tbody>
              </table>
          </body>
          </html>
    

    Доступ после перезапуска сервераhttp://localhost:3333/userAll select_table_page.pngПосле того, как эта страница выйдет, часть отображения будет готова.

  • Функция запроса завершена, и теперь мы можем реализовать новую функцию.

3. Добавьте данные в базу данных mysql

  • Давайте сначала закончим писать дополнительную часть страницы table.html.

      <form action="/addUser">
          <label for="name">
              用户名:
              <input type="text" name="name" placeholder="请输入用户名">
          </label>
          <label for="age">
              年龄:
              <input type="number" name="age" min="0" placeholder="请输入年龄">
          </label>
          <input type="submit" value="添加">
      </form>
    
  • На этот раз страница выглядит так

    table_search.png

  • После того как мы введем логин и возраст и нажмем кнопку добавить, браузер отправит данные на/addUserДалее в этом маршруте мы получаем параметры, переданные из внешнего интерфейса в test.js, и сохраняем параметры в базе данных. затем обновите страницу

      //请求 /addUser 接受前端传过来的数据,并且把数据持久化到数据库中
      router.get("/addUser", async ctx => {
          const { name, age } = ctx.query
          // 判断 name 和 age是否有值,都有值时,数据存入数据库,刷新表格页面
          // 否则直接返回到表格页面
          if(name && age) {
          await resDb("INSERT INTO user values(null,?,?)",[name, age])
          }
           //重定向路由,到 userAll
          ctx.redirect("/userAll")
      })
    
  • УлучшитьresDbнадежность, мы обновили этот метод

      function resDb(sql, params) {
          return new Promise((resolve,reject) => {
              let sqlParamsList = [sql]
              if(params) {
                  sqlParamsList.push(params)
              }
              connection.query(...sqlParamsList, (err,res) => {
                  if(err) {
                      reject(err)
                  }else {
                      resolve(res)
                  }
              })
          })
      }
    
  • Этот способ после апгрейда подходит для промиса CRUD, о функциях модификации и удаления мы, конечно же, поговорим ниже.

  • На данный момент наша новая функция завершена, тогда давайте взглянем на волну скриншотов и разберемся в логике

    add_before.png

    add_bef.png

4. Обновить данные по id

  • Для обновления фронтальной части данных мы не будем писать модальное окно, а напрямую напишем аналогичную новую форму для реализации операции обновления.На самом деле новая и обновленная функции очень похожи, разница только в способе написания sql

  • Давайте сначала преобразуем страницу table.html

          <form action="/updateUser">
              <label for="id">
                  id:
                  <input type="number" name="id" placeholder="请输入要更新的ID">
              </label>
              <label for="name">
                  用户名:
                  <input type="text" name="name" placeholder="请输入用户名">
              </label>
              <label for="age">
                  年龄:
                  <input type="number" name="age" min="0" placeholder="请输入年龄">
              </label>
              <input type="submit" value="修改">
          </form>
    
  • Давайте посмотрим на код за кулисами

      //请求 /updateUser 接受前端传过来的数据,并且把数据持久化到数据库中
      router.get("/updateUser", async ctx => {
          const { id, name, age } = ctx.query
          // 判断 id, name 和 age是否有值,都有值时,更新数据库中的数据,刷新表格页面
          // 否则直接返回到表格页面
          if(id, name && age) {
          await resDb("UPDATE user SET name=?, age=? WHERE id=?",[name, age, id])
          }
          //重定向路由,到 userAll
          ctx.redirect("/userAll")
      })
    
  • Логика кода такая же, как и логика новой части,

  • Я только что писал новый и обновленный код sql, вы увидите, что в нем есть операторы sql.?заполнитель, второй массив параметров?Содержимое, соответствующее заполнителю. Так что на этот раз у всех точно возникнет такой вопрос, а почему бы нам напрямую не прописать параметры, переданные из фронтенда. Это должно быть так хлопотно.

  • На самом деле это написано в виде заполнителейsqlсостоит в том, чтобы предотвратитьsql注入,Связанныйsql注入Вы можете обратиться к этой статьеПринцип и предотвращение SQL-инъекций

5. Удалить один фрагмент данных по идентификатору

  • Старые правила, давайте сначала преобразуем страницу table.html

      <table class="table" border="1"  cellspacing="0">
          <thead>
              <tr>
                  <th>id</th>
                  <th>姓名</th>
                  <th>年龄</th>
                  <th>操作</th>
              </tr>
          </thead>
          <tbody>
              {% for user in userAll %}
              <tr >
                  <td>{{user.id}}</td>
                  <td>{{user.NAME}}</td>
                  <td>{{user.age}}</td>
                  <td>
                      <a href={{'/delete/'+user.id}}>删除</a>
                  </td>
              </tr>
              {% endfor %}
          </tbody>
      </table>
    
  • Посмотрите на эффект страницыdel_page.png

  • Старые правила, давайте посмотрим на код в фоновом режиме

      //请求/delete/:id  接受前端传过来的数据,并且把对应的id的数据删掉
      router.get("/delete/:id", async ctx => {
          const { id } = ctx.params
          // 判断 id否有值,有值时,根据id删除数据库中的数据,刷新表格页面
          // 否则直接返回到表格页面
          if(id) {
          await resDb("DELETE FROM user WHERE id=?",[id])
          }
          //重定向路由,到 userAll
          ctx.redirect("/userAll")
      })
    
  • Пока что написаны добавления, удаления, ревизии (CRUD) таблицы.

6. Полный код

  • Структура каталоговdir结构.png
  • package.json
        {
           "koa": "^2.13.1",
           "koa-nunjucks-2": "^3.0.2",
           "koa-router": "^10.0.0",
           "mysql": "^2.18.1"
         }
    
  • test.js
        const Koa = require("koa")
        const Router = require("koa-router")
        const mysql = require("mysql")
        const koaNunjucks = require('koa-nunjucks-2');
        const path = require('path');

        const app = new Koa();
        const router = new Router();

        // mysqljs 连接 mysql数据库
        let connection = mysql.createConnection({
            host: '127.0.0.1', // mysql所在的主机,本地的话就是 127.0.0.1 或者 localhost, 如果数据库在服务器上,就写服务器的ip
            user: 'root', // mysql的用户名 默认root
            password: 'mysql密码', // mysql的密码
            database: 'db1' // 你要连接那个数据库
        })

        // 连接 mysql
        connection.connect(err=>{
            // err代表失败
            if(err) {
                console.log("数据库初始化失败");
            }else {
                console.log("数据库初始化成功");
            }
        })

        // 因为 mysqljs不支持 Promise方式CRUD数据
        // 所以我们做一个简单的封装
        function resDb(sql, params) {
            return new Promise((resolve,reject) => {
                let sqlParamsList = [sql]
                if(params) {
                    sqlParamsList.push(params)
                }
                connection.query(...sqlParamsList, (err,res) => {
                    if(err) {
                        reject(err)
                    }else {
                        resolve(res)
                    }
                })
            })
        }

         // 注入 nunjucks 模板引擎
         app.use(koaNunjucks({
            ext: 'html', // html文件的后缀名
            path: path.join(__dirname, 'views'), // 视图文件放在哪个文件夹下
            nunjucksConfig: {
              trimBlocks: true // 自动去除 block/tag 后面的换行符
            }
          }));

        //请求 /userAll 的时候返回数据
        router.get("/userAll", async ctx => {
            const userAll = await resDb("SELECT * FROM user")
            await ctx.render("table",{userAll})
        })

        //请求 /addUser 接受前端传过来的数据,并且把数据持久化到数据库中
        router.get("/addUser", async ctx => {
            const { name, age } = ctx.query
            // 判断 name 和 age是否有值,都有值时,数据存入数据库,刷新表格页面
            // 否则直接返回到表格页面
            if(name && age) {
            await resDb("INSERT INTO user values(null,?,?)",[name, age])
            }
            //重定向路由,到 userAll
            ctx.redirect("/userAll")
        })

        //请求 /updateUser 接受前端传过来的数据,并且把数据持久化到数据库中
        router.get("/updateUser", async ctx => {
            const { id, name, age } = ctx.query
            // 判断 id, name 和 age是否有值,都有值时,更新数据库中的数据,刷新表格页面
            // 否则直接返回到表格页面
            if(id, name && age) {
            await resDb("UPDATE user SET name=?, age=? WHERE id=?",[name, age, id])
            }
            //重定向路由,到 userAll
            ctx.redirect("/userAll")
        })

        //请求/delete/:id  接受前端传过来的数据,并且把对应的id的数据删掉
        router.get("/delete/:id", async ctx => {
            const { id } = ctx.params
            // 判断 id否有值,有值时,根据id删除数据库中的数据,刷新表格页面
            // 否则直接返回到表格页面
            if(id) {
            await resDb("DELETE FROM user WHERE id=?",[id])
            }
            //重定向路由,到 userAll
            ctx.redirect("/userAll")
        })

        //测试代码
        router.get("/hello", ctx => {
            ctx.body = "hello"
        })


        app
        .use(router.routes())
        .use(router.allowedMethods())
        .listen(3333,()=>{
            console.log("server running port:" + 3333);
        })
  • views/table.html

      <!DOCTYPE html>
          <html lang="en">
    
          <head>
              <meta charset="UTF-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>Document</title>
              <style>
                  .table {
                      width: 500px;
                  }
    
                  td {
                      text-align: center;
                  }
              </style>
          </head>
    
              <body>
                  <form action="/addUser" autocomplete="off">
                      <label for="name">
                          用户名:
                          <input type="text" name="name" placeholder="请输入用户名">
                      </label>
                      <label for="age">
                          年龄:
                          <input type="number" name="age" min="0" placeholder="请输入年龄">
                      </label>
                      <input type="submit" value="添加">
                  </form>
                  <form action="/updateUser" autocomplete="off">
                      <label for="id">
                          id:
                          <input type="number" name="id" placeholder="请输入要更新的ID">
                      </label>
                      <label for="name">
                          用户名:
                          <input type="text" name="name" placeholder="请输入用户名">
                      </label>
                      <label for="age">
                          年龄:
                          <input type="number" name="age" min="0" placeholder="请输入年龄">
                      </label>
                      <input type="submit" value="修改">
                  </form>
                  <table class="table" border="1" cellspacing="0">
                      <thead>
                          <tr>
                              <th>id</th>
                              <th>姓名</th>
                              <th>年龄</th>
                              <th>操作</th>
                          </tr>
                      </thead>
                      <tbody>
                          {% for user in userAll %}
                          <tr>
                              <td>{{user.id}}</td>
                              <td>{{user.NAME}}</td>
                              <td>{{user.age}}</td>
                              <td>
                                  <a href={{'/delete/'+user.id}}>删除</a>
                              </td>
                          </tr>
                          {% endfor %}
                      </tbody>
                  </table>
              </body>
          </html>
    

7. Пишите в конце

  • Когда вы это видите, во-первых, вы очень настойчивый человек.В этой статье нет иллюстраций.Все это реализация кода и скриншоты страниц.Если вы видите это от начала до конца, поставьте себе лайк.
  • В этой статье подробно рассказывается, как nodejs подключается к базе данных mysql и реализует механизм шаблонов на основе增删改查функцию, и просто сделать промис-пакет для результата, возвращаемого базой данных, а также ввести соответствующие плагины, используемые в koa и его экземплярах

8. Обзор прошлого