Как организовать связь с базой данных в Node.Js? - PullRequest
0 голосов
/ 08 июля 2019

У меня проблема с проектированием связи с базой данных MySQL в приложении моего Nodejs. Самая большая проблема заключается в том, что запросы асинхронны, поэтому разработка моих проектов усложняется. Например, у меня есть excercises.js

EXCERCISES.JS

var express = require('express');
var database = require('../database/database.js');

var router = express.Router();

console.log(database.db(saveDbData))
/* GET users listing. */
router.get('/', function(req, res, next) {

  res.render('exercises',{title: 'Exercises', ex: #DATABASE RESULT});
});

module.exports = router;

В случае необходимости записать результат запроса в поле ex .

Затем я пишу модуль для обработки подключения mysql

DATABASE.JS

var pool = mysql.createPool({
    connectionLimit: 10000,
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'Example'
});

var results;

var db = function(){
    pool.query('SELECT name FROM Exercises', function(error, results, fields){
    if (error) throw error;
    res = results;
})
    return res;
}

module.exports = {
    db: db,
}

Очевидно, это не работает, потому что pool.query асинхронный. Единственная альтернатива, которую я нашел в Интернете, это что-то вроде этого:

EXERCISES.JS


var pool = mysql.createPool({
    connectionLimit: 10000,
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'Example'
});


pool.query('SELECT name FROM Exercises', function(error, results, fields){
    if (error) throw error;
    router.get('/', function(req, res, next) {
        res.render('exercises',{title: 'Exercises', ex: result[0].name});
    }); 
})

Но при этом части mysql и части маршрутизации / рендеринга смешиваются. Это все еще хорошо продуманное решение? Есть ли более элегантные решения?

EDIT

Я изменил файлы и использовал Promise вот так

EXERCISES.JS

var express = require('express');
var database = require('../database/database.js');

var router = express.Router();


var data = database.db()
.then(
    function(data){
        console.log("Resolved");
        /* GET users listing. */
        router.get('/', function(req, res, next) {
            res.render('exercises',{title: 'Exercises', ex: data[0].name});
        });
    })
.catch(
    error => console.error(error));



module.exports = router;

DATABASE.JS

ar mysql = require('mysql');


var pool = mysql.createPool({
    connectionLimit: 10000,
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'Example'
});

var res;
var db = function(){
    return new Promise(function(resolve, reject){
        pool.query('SELECT name FROM Exercises', function(error, results, fields){
            if (error) reject(error);
            resolve(results)

        })
    })
}


module.exports = {
    db: db,
}

И это работает, но я не думаю, что это лучшее решение. Например, что если я хочу получить данные для рендеринга из большего количества запросов? Я новичок в этих технологиях, поэтому не могу найти лучший способ интеграции рендеринга базы данных и HTML-страниц.

Ответы [ 2 ]

0 голосов
/ 09 июля 2019

Задумывались ли вы, почему все веб-фреймворки в узле требуют, чтобы вы возвращали ответы, используя объект res вместо return?Это потому, что все веб-фреймворки ожидают, что вам нужно сделать что-то асинхронное.

Рассмотрим дизайн веб-фреймворка, похожий на Laravel (PHP) или Spring Framework (Java):

// Theoretical synchronous framework API:
app.get('/path', function (request) {
    return "<html><body> Hello World </body></html>";
});

Тогда, если выЕсли вы захотите сделать что-то асинхронное, вы столкнетесь с проблемой того, что извлекаемые вами данные не вернутся к тому времени, когда вам нужно вернуть HTTP-запрос:

// Theoretical synchronous framework API:
app.get('/path', function (request) {
    return ??? // OH NO! I need to return now!!
});

Именно по этой причине веб-фреймворки вJavaScript не действует на возвращаемые значения.Вместо этого он передает вам обратный вызов для вызова, когда вы закончите:

// Express.js
app.get('/path', function (request, response) {
    doSomethingAsync((err, result) => {
        response.send(result);
    });
});

Так что для вашего кода вам просто нужно сделать:

router.get('/', function(req, res) {
    pool.query('SELECT name FROM Exercises', function(error, results, fields){
        if (error) throw error;   
        res.render('exercises',{title: 'Exercises', ex: result[0].name});
    }); 
});

Экспорт базы данных

Экспортировать базу данных так же просто, как экспортировать pool:

db.js

var pool = mysql.createPool({
    connectionLimit: 10000,
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'Example'
});

module.exports = {
    pool: pool
}

exerc.js

let db = require('./db');

// you can now use db.pool in the rest of your code
// ..

Повторное использование ваших запросов

Вместо того, чтобы кодировать операторы SELECT в ваших контроллерах (маршрутах), вы можете (и должны) кодировать их в ваших модулях дб:

дб.js

var pool = mysql.createPool({
    connectionLimit: 10000,
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'Example'
});

function getExerciseNames (callback) {
    pool.query('SELECT name FROM Exercises',callback);
}

module.exports = {
    pool: pool
}

Тогда в вашей логике контроллера вам просто нужно сделать:

router.get('/', function(req, res) {
    db.getExerciseNames(function(error, results, fields){
        if (error) throw error;   
        res.render('exercises',{
            title: 'Exercises',
            ex: result[0].name
        });
    }); 
});

Кэширование

Если вы собираетесь запрашивать только БД один раз для кэширования значения упражнений, а затем не инвертировать поток экспресс-маршрутизации.Вместо этого реализуйте кэширование на своем уровне базы данных:

db.js :

var exerciseNamesCache = [];
var exerciseNamesFields = [];

function getExerciseNames (callback) {
    if (exerciseNamesCache.length > 0 && exerciseNamesFields.length > 0) {
        callback(null, exerciseNamesCache, exerciseNamesFields);
    }
    pool.query('SELECT name FROM Exercises',function(error, results, fields){
        if (!error) {
            exerciseNamesCache = results;
            exerciseNamesFields = fields;
        }
        callback(error, results, fields);
    });
}

Обещания

Обещания - это шаблон дизайна дляобработка обратных вызовов.Это сравнимо с Futures Java (CompletionStage и т. Д.) Только намного более легковесным.Если используемый вами API возвращает обещание вместо принятия обратного вызова, вам нужно вызвать res.render() внутри метода обещания .then():

router.get('/', function(req, res, next) {
    doSomethingAsync()
        .then(function(result){
            res.send(result);
        })
        .catch(next); // remember to pass on asynchronous errors to next()
});

Если используемый вами API принимает обратный вызов, тогдапогода или нет, вы заверните его в обещание, это больше вопрос вкуса.Лично я бы не стал этого делать, если вы не используете другой API, который возвращает обещание.

async / await

Одним из преимуществ обещаний является то, что вы можете использовать их с await.Express специально работает хорошо с async / await.Просто помните, что вы можете использовать await только внутри функции, отмеченной async:

router.get('/', async function(req, res, next) {
    let result = await doSomethingAsync();
    res.send(result);
});

Множественные асинхронные операции

Получение нескольких асинхронных данных может быть простым:

router.get('/', function(req, res, next) {
    doSomethingAsync(function(result1){
        doSomethingElse(function(result2) {
            res.json([result1, result2]);
        });
    });
});

С обещаниями, которые будут:

router.get('/', function(req, res, next) {
    doSomethingAsync()
        .then(function(result1){
            return doSomethingElse()
                .then(function(result2) {
                    return [result1, result2];
                 });
         })
         .then(function(results){
             res.json(results);
         })
         .catch(next);
});

Но оба приведенных выше кода выполняют запросы последовательно (получить результат 1, а затем получить результат 2).Если вы хотите получать обе данные параллельно, вы можете сделать это с помощью Promises:

router.get('/', function(req, res, next) {
    Promise.all([
        doSomethingAsync(),  // fetch in parallel
        doSomethingElse()
    ])
    .then(function(results){
        res.json(results);
    });
})

С обратными вызовами это немного сложнее.Существует шаблон проектирования, который вы можете использовать, и кто-то фактически реализовал его как библиотеку под названием async.js , но зачастую самым простым решением является обернуть их в Promises и использовать Promise.all().Тем не менее, проверьте async.js, поскольку он имеет функциональные возможности, полезные для таких вещей, как пакетные запросы, выполнение асинхронных операций, когда условие истинно и т. Д. (Аналог обещания для этой библиотеки: async-q )

0 голосов
/ 09 июля 2019

Вы можете использовать модули npm для выполнения асинхронной задачи с MySQL. Я рекомендовал выбрать sequilize или jm-ez-mysql . Если вы выберете jm-ez-mysql , тогда ваша структура кода будет выглядеть как

server.js

require('./config/database.js');

. / Конфигурации / database.js

const sql = require('jm-ez-mysql');

// Init DB Connection
const connection = My.init({
  host: process.env.DBHOST,
  user: process.env.DBUSER,
  password: process.env.DBPASSWORD,
  database: process.env.DATABASE,
  dateStrings: true,
  charset: 'utf8mb4',
  timezone: 'utc',
  multipleStatements: true,
});

module.exports = {
  connection,
};

После этого вы можете использовать MySQL асинхронно.

. / Exercise.js

const sql = require('jm-ez-mysql');

const exerciseUtil = {};

exerciseUtil.searchUserById = async (id) => {
  try {
    // table name, db fields, condition, values
    const result = await sql.first('users', ['id, name, email'], 'id = ?', [id]);
    return result; // Which return your result in object {}
  } catch (err) {
    console.log(err);
    throw err;
  }
};

module.exports = exerciseUtil;

Надеюсь, это поможет. Удачного кодирования:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...