Nodejs interactúa con la base de datos MySQL

Este artículo presentará cómo usar mysqljs/mysqlmódulos para realizar la interacción Node.jsentre la aplicación y MySQLla base de datos.

Preparar

  1. Preparar entorno Nodejs, sitio web oficial de Nodejs
  2. Preparar base de datos MySQL, sitio web oficial de MySQL

Instalar

mysqljs/mysqlintroducción del módulo

Este es un módulo de Node.js disponible a través del registro npm .
Este es un controlador de node.js para mysql. Está escrito en JavaScript, no requiere compilación y tiene licencia 100% MIT.

En el directorio raíz de la aplicación Node.js, use el comando npm para instalar el módulo mysql

npm install mysql

usar

1. Conectarse a la base de datos

paso 1: mysqlmódulo de importación

const mysql = require("mysql");

paso 2: llamada para createConnection()crear un objeto de conexión

// createConnection(config),config:Object|String 提供两种创建连接对象的方式
// config 详情参考 https://github.com/mysqljs/mysql#connection-options
// Object 传参方式
const conn = mysql.createConnection({
    
    
    host:"localhost",
    port:3306,
    user:"root",
    password:"1234qwer",
    database:"ex_test"
});

// String 传参方式,可读性差,不推荐
// URL格式的连接配置,如 mysql://user:pass@host/db?debug=true
const conn2 = mysql.createConnection("mysql://root:1234qwer@localhost:3306/ex_test?debug=true");

// 数据库连接配置,也可以使用如下方式进行配置
conn.config.database = "ex_test";

paso 3: llamar conn.connect()para establecer una conexión

// connect()方法接受一个具有err参数的回调函数,如果发生任何错误,它将提供详细的错误
conn.connect(function (err){
    
    
    if(err){
    
    
        console.log(err.message);
    }else{
    
    
        console.log("---------------[conn.connect] succeed.---------------")
    }
})

paso 4: llamada conn.query()para operar la base de datos

// 对数据库的CRUD,都是用query()方法,后边再详细说明
conn.query("select 1",function (err,results){
    
    
    console.log(results);
})

paso 5: llamar para conn.end()cerrar la conexión

// end()接受一个回调函数,并且会在query结束之后才触发,如果query出错,仍然会终止连接,错误会传递到回调函数中处理
conn.end(function (err){
    
    
    if(err){
    
    
        console.log(err.message);
    }else{
    
    
        console.log("---------------[conn.end] succeed.---------------")
    }
});
// 要立即强制连接,可以使用destroy()方法
// destroy()立即终止数据库连接,即使还有query没有完成,它不会像end()方法那样采取任何回调参数
conn.destroy();

2. Operar la base de datos

Todas las operaciones en la base de datos (CRUD, creación de base de datos, creación de tablas, etc.) utilizan el método query(), que recibe tres parámetros: el primer parámetro
: sentencia sql u objeto de opción;
el segundo parámetro: sentencia sql El valor proporcionado por el marcador de posición es un valor único, o una matriz de valores múltiples, o un objeto;
el tercer parámetro: función de devolución de llamada, la función de devolución de llamada tiene 3 parámetros (error, resultado, campos), el error es un error que ocurrió cuando el mensaje de error, resultados es el resultado de la ejecución de sql, campos es el campo involucrado (si lo hay);

Para obtener más información, consulte: https://github.com/mysqljs/mysql#performing-queries

2.1 Crear biblioteca

// 创建数据库 nodejs_mysql_db(不存在时才创建)
let db_sql = "CREATE DATABASE IF NOT EXISTS `nodejs_mysql_db` DEFAULT CHARSET utf8;"
conn.query(db_sql,function (err,results){
    
    
    if(err){
    
    return console.log(err);}
    console.log(results.affectedRows);	// 如果不报错,无论是否已创建 results.affectedRows=1
})

2.2 Crear tabla

// 创建数据表 student (不存在时才创建)
let tb_sql = `CREATE TABLE IF NOT EXISTS students (
    Sid INT(8) NOT NULL AUTO_INCREMENT,
    Sname VARCHAR(10) NOT NULL,
    Sage INT(11) DEFAULT NULL,
    Ssex VARCHAR(10) DEFAULT NULL,
    PRIMARY KEY (Sid)
) ENGINE=INNODB DEFAULT CHARSET=utf8;`
// 上边在创建数据库连接对象时,连接的database是ex_test,这里给切换到新建的nodejs_mysql_db数据库
conn.config.database = "nodejs_mysql_db";
conn.query(tb_sql,function (err,results){
    
    
    if(err){
    
    return console.log(err);}
    console.log(results);
})

2.3 Nuevos datos

// 方式1:每个?占位符 代表1个列属性,使用数组方式传值,数组长度与占位符个数一致,数组的每个元素将依次传给?占位符
// Arrays are turned into list, e.g. ['a', 'b'] turns into 'a', 'b'
let sql_insert1 = "INSERT INTO students (Sname,Sage,Ssex) VALUES (?,?,?)";
let student1 = ["张敏","20","女"];
// console.log(mysql.format(sql_insert1,student1));		// 可以使用 mysql.format() 将sql语句转化为标准sql
let qr1 = conn.query(sql_insert1,student1,function (err,results){
    
    
    if(err){
    
    return console.log(err);}
    let ret = {
    
    affectedRows:results.affectedRows,insertId:results.insertId}
    console.log(ret);
})
console.log(qr1.sql);	// 查看query实际执行的sql语句
// 方式2:每个 代表1组数据,使用数组方式传值
// Nested arrays are turned into grouped lists (for bulk inserts),
// e.g. [['a', 'b'], ['c', 'd']] turns into ('a', 'b'), ('c', 'd')
let sql_insert2 = "INSERT INTO students (Sname,Sage,Ssex) VALUES ?";
let student2 = [["赵莹莹","21","男"],["钱娟娟","19","女"]];
// console.log(mysql.format(sql_insert2,[student2]));
let qr2 = conn.query(sql_insert2,[student2],function (err,results){
    
    
    if(err){
    
    return console.log(err);}
    let ret = {
    
    affectedRows:results.affectedRows,insertId:results.insertId}
    console.log(ret);
})
console.log(qr2.sql);
// 方式3:使用 INSERT INTO ... SET
// Objects are turned into key = 'val' pairs
let sql_insert3 = "INSERT INTO students SET ?";
let student3 = {
    
    Sname:"李少臣",Sage:"21",Ssex:"男"};
// console.log(mysql.format(sql_insert3,student3));
let qr3 = conn.query(sql_insert3,student3,function (err,results){
    
    
    if(err){
    
    return console.log(err);}
    let ret = {
    
    affectedRows:results.affectedRows,insertId:results.insertId}
    console.log(ret);
})
console.log(qr3.sql);

resultado de la operación
Nuevos resultados de ejecución de datos
Resultados de la consulta de nuevos datos de la base de datos

2.4 Consulta de datos

let sql_select = "SELECT * FROM students";
conn.query(sql_select,function (err,results){
    
    
    if(err){
    
    return console.log(err);}
    console.log(results);
})

resultado de la operación
Consultar resultados de datos

2.5 Actualizar datos

let sql_update = "UPDATE students SET ? WHERE Sid = ?";
console.log(mysql.format(sql_update,[{
    
    Ssex:"女"},2]));
conn.query(sql_update,[{
    
    Ssex:"女"},2],function (err,results){
    
    
    if(err){
    
    return console.log(err);}
    console.log(results);
})

resultado de la operación
Actualizar resultados de ejecución de datos
resultado de la consulta de la base de datos

2.6 Eliminar datos

// 方式1:物理删除,即直接将数据记录从数据库中删除
let sql_delete = "DELETE FROM students WHERE Sid = ?";
conn.query(sql_delete,4,function (err,results){
    
    
    if(err){
    
    return console.log(err);}
    console.log(results.affectedRows);
})

// 方式2:逻辑删除,即给students表增加一个status字段,1 标识有效,0 标识无效,默认值为1
// let sql_alter = "ALTER TABLE students ADD status INT(1) DEFAULT 1 AFTER Ssex;"
// conn.query(sql_alter,function (err,results){
    
    
//     if(err){return console.log(err);}
//     console.log(results);
// })
let sql_delete_flag = "UPDATE students SET status = 0 WHERE Sid = ?"
conn.query(sql_delete_flag,3,function (err,results){
    
    
    if(err){
    
    return console.log(err);}
    console.log(results.affectedRows);
})

resultado de la operación
Eliminar resultados de ejecución de datos

3. Conexión del grupo de conexiones

El método de creación/gestión de conexiones uno por uno se introdujo en [1. Conexión a la base de datos] mysql.createConnection(config)El módulo mysql también admite la agrupación de conexiones para la conexión a la base de datos.

3.1 Crear un grupo de conexiones

Crear un grupo de conexiones de base de datosmysql.createPool(config)

const mysql = require("mysql");
const pool = mysql.createPool({
    
    
    connectionLimit:5,	//设置最大连接数为5
    host:"localhost",
    user:"root",
    password:"1234qwer"
})
// 可以通过如下方式新增或更改连接配置
pool.config.connectionConfig.database = "nodejs_mysql_db";

Además de los mismos parámetros que connection-options , pool-option tiene varios parámetros extendidos:

  • acquireTimeout: el número de milisegundos antes de que se agote el tiempo de espera durante la adquisición de la conexión. Esto difiere ligeramente de connectTimeout en que obtener una conexión agrupada no siempre es necesario para establecer una conexión. Si una solicitud de conexión está en cola, el tiempo que la solicitud pasa en la cola no cuenta para este tiempo de espera. (predeterminado: 10000)
  • waitForConnections: determina la acción del grupo cuando no hay conexiones disponibles y se ha alcanzado el límite. Si es verdadero, el grupo pondrá en cola las solicitudes de conexión y lo llamará cuando una solicitud de conexión esté disponible. Si es falso, el grupo devolverá la llamada inmediatamente y devolverá un error. (predeterminado: verdadero)
  • connectionLimit: El número máximo de conexiones creadas a la vez. (predeterminado: 10)
  • queueLimit: el número máximo de solicitudes de conexión que el grupo pondrá en cola antes de devolver un error de getConnection. Si se establece en 0, no hay límite para el número de solicitudes de conexión en cola. (predeterminado: 0)

3.2 Uso del grupo de conexiones

Hay dos formas de usar la consulta del grupo de conexiones
Método 1: Usar directamente pool.query()

// 直接使用(多个pool.query()可能是不同的连接,它们并行运行)
pool.query("SELECT Sid,Sname FROM students",function (err,results){
    
    
    if(err){
    
    return console.log(err)}
    console.log("All students: ",results);
    // pool.end();	//结束连接池的所有连接
})

Método 2: conexión compartida pool.getConnection()

// 共享连接(使用pool.getConnection()可以为后续查询共享连接状态)
// pool.getConnection() -> connection.query() -> connection.release()
pool.getConnection(function (err,conn){
    
    
    conn.query("SELECT Sid,Sname FROM students LIMIT 1",function (err,results){
    
    
        if(err){
    
    return console.log(err)}
        console.log(results);
    })
    conn.query("SELECT COUNT(*) AS count FROM students",function (err,results){
    
    
        if(err){
    
    return console.log(err)}
        console.log("total students is: "+results[0].count);
    })
    console.log({
    
    threadId:conn.threadId,state:conn.state});
    conn.release();	// 释放连接到连接池
    // pool.end();
})

3.3 Cerrar el grupo de conexiones

En la Sección 3.2, conn.release()libere la conexión, la conexión liberada se devolverá al grupo de conexiones y otros usuarios podrán continuar usándola. Para cerrar una conexión y eliminarla del grupo, use conn.destroy(). Si desea cerrar el grupo de conexiones, puede usar pool.end()el método, que recibe una función de devolución de llamada, y la función de devolución de llamada tiene un parámetro err, que se puede usar para saber cuándo finalizan todas las conexiones. Una vez que se llama a pool.end, pool.getConnection y otras operaciones ya no se pueden realizar.

pool.end(function (err){
    
    
    if(err){
    
    return console.log(err)}
    console.log("The pool connections are terminated now.")
})

4. Reconexión de la base de datos

La base de datos puede verse interrumpida por motivos como tiempo de inactividad, reinicio, tiempo de espera de conexión, etc., lo que requiere un mecanismo de reconexión para mejorar la solidez de la aplicación.
Puede monitorear el evento de error de la conexión en el código para determinar si el código de error devuelto es: PROTOCOL_CONNECTION_LOST, si usa setTimeout para volver a conectarse en 3 segundos.

const mysql = require("mysql");
let db_config = {
    
    
    host: 'localhost',
    user: 'root',
    password: '1234qwer',
    database: 'nodejs_mysql_db'
};

let connection;
let conn_times = 0;

function handleDisconnect() {
    
    
    connection = mysql.createConnection(db_config);
    connection.connect(function(err) {
    
    
        conn_times++;
        if(err) {
    
    
            console.log("第 " + conn_times + " 次连接数据库...失败!");
            console.log("连接数据库失败,3s后即将尝试重连...");
            setTimeout(handleDisconnect, 3000);
        }else{
    
    
            console.log("第 " + conn_times + " 次连接数据库...成功!");
            conn_times=0;
        }
    });
    connection.on('error', function(err) {
    
    
        console.log("数据库连接异常!!!");
        if(err.code === 'PROTOCOL_CONNECTION_LOST') {
    
    
            handleDisconnect();
        }
    });
}
handleDisconnect();

volver a conectar la base de datos
Como se puede ver en los resultados de la ejecución, intentará volver a conectarse al servicio de la base de datos cada 3 segundos hasta que la conexión sea exitosa.

5. Escapar valores de consulta

Referencia: escape-query-values
​​​​Para evitar ataques de inyección SQL, siempre se debe escapar cualquier dato proporcionado por el usuario antes de usarlo en una consulta SQL. Puede usar mysql.escape(), conexión.escape(), pool.escape()

let Sid = "the Sid provided by user";
let sql1 = "SELECT * FROM students WHERE Sid = " + conn.escape(Sid);
conn.query(sql1,function (err,results){
    
    
    if (err) throw err;
    // ...
})

También puede usar ? como marcador de posición para el valor que desea escapar, y múltiples marcadores de posición corresponden al valor pasado en orden uno a uno

// 只有1个占位符,conn.query()的第2个参数的[]可以省略
let Sid = "the Sid provided by user";
let sql2 = "SELECT * FROM students WHERE Sid = ?";
conn.query(sql2,Sid,function (err, results){
    
    
    if (err) throw err;
    // ...
});

// 多个占位符,conn.query()的第2个参数是数组
let values = ["陈粒","19","女",5];
let sql3 = "UPDATE students SET Sname=?,Sage=?,Ssex=? WHERE Sid=?"
conn.query(sql3,values,function (err,results){
    
    
   if (err) throw err;
   // ...
})

Los diferentes tipos de valores se escaparán de diferentes maneras, de la siguiente manera:

  • Los números siguen siendo los mismos
  • Booleanos convertidos a verdadero/falso
  • Objetos de fecha convertidos a la cadena 'YYYY-mm-dd HH:ii:ss'
  • Los búferes se convierten en una cadena hexadecimal, por ejemplo, X'0fa5'
  • Cuerdas de escape seguro
  • Las matrices se convierten en listas, por ejemplo, ['a', 'b'] se convierten en 'a', 'b'
  • Matrices anidadas Las matrices anidadas se convierten en listas agrupadas (inserción por lotes), por ejemplo, [['a', 'b'], ['c', 'd']] se convierten en ('a', 'b'), ( 'c', 'd')
  • Objetos (Objetos con el método .toSqlString), el valor de retorno de .toSqlString() se usará como SQL sin procesar
  • Cada atributo de Objetos se convertirá en un par clave-valor clave = 'val', el valor del atributo es una función que se omitirá y el valor del atributo es un objeto que se convertirá en una cadena al llamar a toString()
  • indefinido / nulo a NULL

El uso de las reglas de conversión anteriores puede hacer algunas cosas inteligentes, por ejemplo:

// Nested arrays 嵌套数组转化为分组列表(批量插入),e.g. [['a', 'b'], ['c', 'd']] 转化为 ('a', 'b'), ('c', 'd')
// 批量插入,以分组列表的方式替换占位符
let sql_insert2 = "INSERT INTO students (Sname,Sage,Ssex) VALUES ?";
let student2 = [["赵莹莹","21","男"],["钱娟娟","19","女"]];
conn.query(sql_insert2,[student2],function (err,results){
    
    
    if(err){
    
    return console.log(err);}
	// ...
})

// Objects 每个属性将转化为 key = 'val' 键值对,属性值是函数的将跳过,属性值是object的将调用toString()转为字符串
// 以键值对的方式使用替换占位符
let sql_insert3 = "INSERT INTO students SET ?";
let student3 = {
    
    Sname:"李少臣",Sage:"21",Ssex:"男"};
let qr3 = conn.query(sql_insert3,student3,function (err,results){
    
    
    if(err){
    
    return console.log(err);}
	// ...
})

//  Objects (含有.toSqlString方法的Objects),.toSqlString()的返回值将作为 raw SQL 使用
// 下边这个例子是官网给的
let CURRENT_TIMESTAMP = {
    
     toSqlString: function() {
    
     return 'CURRENT_TIMESTAMP()'; } };
let sql = mysql.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]);
console.log(sql); // UPDATE posts SET modified = CURRENT_TIMESTAMP() WHERE id = 42

6. Identificadores de escape

Referencia: escape-query-identifiers
Si no puede confiar en los identificadores SQL (base de datos/tabla/nombres de columna) proporcionados por el usuario, puede usar mysql.escapeId(identificador), conexión.escapeId(identificador) o pool.escapeId( identificador) por el cual se escapa

let sorter = 'date';
let query = 'SELECT * FROM posts ORDER BY ' + mysql.escapeId(sorter); 
console.log(query); // SELECT * FROM posts ORDER BY `date`

// 支持添加特定的标识符
let sorter = 'date';
let query = 'SELECT * FROM posts ORDER BY ' + mysql.escapeId('posts.' + sorter);
console.log(query); // SELECT * FROM posts ORDER BY `posts`.`date`

// 可以使用??作为标识符的占位符
let userId = 1;
let columns = ['username', 'email'];
let query = connection.query('SELECT ?? FROM ?? WHERE id = ?', [columns, 'users', userId], function(err, results) {
    
    
  // ...
}); 
console.log(query.sql); // SELECT `username`, `email` FROM `users` WHERE id = 1

7. Prepara consultas

Referencia: preparación de consultas
para preparar consultas, esta función elegirá el método de escape apropiado para escapar de los parámetros

let sql = "SELECT * FROM ?? WHERE ?? = ?";
let inserts = ['users', 'id', userId];
sql = mysql.format(sql, inserts);

8. Formato personalizado

Referencia: formato personalizado
Si prefiere usar otro tipo de formato de escape de consulta, puede usar la opción de configuración de conexión para definir una función de formato personalizado

connection.config.queryFormat = function (query, values) {
    
    
  if (!values) return query;
  return query.replace(/\:(\w+)/g, function (txt, key) {
    
    
    if (values.hasOwnProperty(key)) {
    
    
      return this.escape(values[key]);
    }
    return txt;
  }.bind(this));
};
connection.query("UPDATE posts SET title = :title", {
    
     title: "Hello MySQL" });

9. Consulta de declaraciones múltiples

Referencia: consultas de declaraciones múltiples
Debido a que las consultas de declaraciones múltiples son vulnerables a los ataques de inyección SQL, el valor predeterminado no está permitido, puede usar const connection = mysql.createConnection({multipleStatements: true})esta función para habilitar

connection.query('SELECT 1; SELECT 2', function (error, results, fields) {
    
    
  if (error) throw error;
  // `results` is an array with one element for every statement in the query:
  console.log(results[0]); // [{1: 1}]
  console.log(results[1]); // [{2: 2}]
});

Presentemos tanto por ahora, para un uso más detallado, consulte el sitio web oficial: mysqljs/mysql

Otros materiales de referencia:
nodejs que opera la base de datos MySQL
Notas de estudio de Nodejs (4) - interacción con MySQL (felixge/node-mysql)
uso de mysql en nodejs

Supongo que te gusta

Origin blog.csdn.net/B11050729/article/details/129352962
Recomendado
Clasificación