likes
comments
collection
share

mysql2 的使用及对查询结果的转换

作者站长头像
站长
· 阅读数 22

安装

pnpm add mysql2

连接数据库

安装完毕后即可导入使用,同在 Navicat 中一样,想要操作数据库的前提是得连接上数据库。可以通过 createConnection 方法,传入选项对象创建连接:

注意,如果端口号不是默认的 3306,选项对象中可以添加 port 配置。另外还有很多配置,可查看 mysql 这个库的使用说明

查询

单表查询

连接上数据库后,就可以进行查询了,比如我们想查询表 members 的信息:

mysql2 的使用及对查询结果的转换

可以将 sql 语句作为第 1 个参数传给 connection.query(),并通过第 2 个参数来接收查询结果:

connection.query('SELECT * FROM `members`', function (err, results, fields) {
  if (err) {
    console.log(err)
    return
  }
  console.log(results) // 结果集
  console.log(fields) // 额外的元数据(如果有的话)
})

查询结果中,results 是一个数组,其子项为表 members 的每一条数据转为的对象 :

mysql2 的使用及对查询结果的转换

fields 也是一个数组,子项是每个字段的一些信息:

mysql2 的使用及对查询结果的转换

多表查询

上面是对单张表的查询,如果查询���张表,执行的 sql 语句如下:

SELECT * FROM members
  JOIN singer_and_city sac ON members.id = sac.singer_id 
  JOIN regions ON sac.city_id = regions.id WHERE members.id = 1;

则在 Navicat 中的结果如下:

mysql2 的使用及对查询结果的转换

通过 mysql2 在 node 程序中执行:

connection.query(
  'SELECT * FROM members JOIN singer_and_city sac ON members.id = sac.singer_id JOIN regions ON sac.city_id = regions.id WHERE members.id = 1;',
  function (err, results) {
    console.log(results)
  }
)

结果如下:

mysql2 的使用及对查询结果的转换

可以看到出问题了,比如有 3 个字段的名称都为 id,最终就展示了表 regions 的 id;有 2 个字段的名称都为 name,最终展示的是 regions 的 name。为了解决这个问题,我们可以通过给字段设置别名的方式解决,但其实关于地点的信息,也就是存储在 regions 表的信息,应该另外包裹成一个 region 对象,放置在每条数据对象中。

将查询结果转换为对象

如下,使用聚合函数 JSON_OBJECT(),创建一个对象,通过 AS 命名为 region,将 regions 的 id 信息赋值给 regionid,将 regions 的 name 信息赋值给 regionname

SELECT 
	members.id, 
	members.`name`, 
	JSON_OBJECT('id', regions.id, 'name', regions.name) AS region 
	FROM members
	JOIN singer_and_city sac ON members.id = sac.singer_id 
	JOIN regions ON sac.city_id = regions.id WHERE members.id = 1;

在 Navicat 执行上述语句得到的结果如下:

mysql2 的使用及对查询结果的转换

在 node 程序中运行:

connection.query(
  "SELECT members.id, members.`name`, JSON_OBJECT('id', regions.id, 'name', regions.name) AS region FROM members JOIN singer_and_city sac ON members.id = sac.singer_id JOIN regions ON sac.city_id = regions.id WHERE members.id = 1;",
  function (err, results, fields) {
    console.log(results)
  }
)

得到的结果如下:

mysql2 的使用及对查询结果的转换

将查询结果转换为数组

为了方便演示,所以上面的演示中我只查看了 Jay 的相关信息,如果去掉查询条件 WHERE members.id = 1,得到的结果通过 node 执行将是:

mysql2 的使用及对查询结果的转换

在 Navicat 中查询则是:

mysql2 的使用及对查询结果的转换

可以看到,关于歌手的 id 和 name 信息是存在重复的,我们可以对查询结果进一步地组织,按照歌手 id 进行分组,然后对 region 字段使用聚合函数 JSON_ARRAYAGG() 处理,转成数组:

SELECT 
	members.id, 
	members.`name`, 
	JSON_ARRAYAGG(JSON_OBJECT('id', regions.id, 'name', regions.name)) AS regionList
	FROM members
	JOIN singer_and_city sac ON members.id = sac.singer_id 
	JOIN regions ON sac.city_id = regions.id
	GROUP BY members.id;

这样在 Navicat 中得到的结果如下:

mysql2 的使用及对查询结果的转换

通过 node 执行得到的就是如下所示的数组:

mysql2 的使用及对查询结果的转换

通过接口的形式在浏览器中查看,可以看到结果是我们所希望的:

mysql2 的使用及对查询结果的转换

预处理语句(Prepared Statements)的使用

mysql2 的一个特性就是可以使用如下所示的,在查询条件部分使用了 ? 来代替具体值的预处理语句:

SELECT * FROM members WHERE `level` > ?;

在 mysql2 中,通过 connection.execute()来执行预处理语句,在第 2 个参数中传入具体的查询参数,因为可能有多个条件,所以是个数组:

connection.execute(
  'SELECT * FROM members WHERE `level` > ?;',
  [1],
  function (err, results, fields) {
    console.log(results)
  }
)

执行结果如下:

mysql2 的使用及对查询结果的转换

使用预处理语句的好处

提高性能

预处理语句会被 MySQL 编译并储存,当给 ? 提供了具体的值时再真正执行查询。所以如果多次执行同样的查询,可以有效地节省准备查询时间,获得更高的性能。

防止 SQL 注入

假设客户端网站有个输入框,只有输入正确的 name(比如 Jay)才能获取对应的信息,服务器端会使用如下语句进行查询:

SELECT * FROM members WHERE `name` = 'Jay';

那么法外狂徒张三就会开动他的小脑筋,尽管他不知道表 members 里到底存储了哪些 name,但他只要想办法让查询时的语句如下:

SELECT * FROM members WHERE `name` = 'Jay' OR 'a'!='b';

也就是在查询条件中多注入了一个判断条件 OR 'a'!='b',那么对于 members 中的每一条记录来说,'a'!='b' 都是成立的,他就可以轻松获取 members 里的所有数据了。 但是如果使用了预处理语句:

SELECT * FROM members WHERE `name` = ?;

MySQL 在解析时就知道查询只有一个条件,即 name 是否等于某值,张三想要再使用 OR 添加判断条件的企图就难以得逞。

连接池(connection pools)的使用

同一时间我们的服务器可能被多个客户端访问,如果使用 mysql.createConnection() 与数据库只建立一个连接,就可能导致某个请求发起时,连接正被占用。解决这一问题,我们可以使用 mysql2 提供的连接池:

连接池通过重用以前的连接来帮助减少连接到 MySQL 服务器所花费的时间,当你完成它们时让它们保持打开而不是关闭。

创建方法如下:

其中 connectionLimit 用于指定创建连接的最大数量。之后的使用和建立单个连接的情况是一样的,只需要把 connection 替换成 pool 即可。如此,当有新的请求时,就会去连接池里寻找闲置的连接来查询数据库。

判断连接是否成功

我们可以通过 pool.getConnection() 来从池中获取连接:

pool.getConnection((err, conn) => {
  // 获取连接错误
  if (err) {
    console.log(err)
    return
  }
  // 连接获取未报错
  conn.connect(err => {
    if (err) {
      console.log(err)
      return
    }
    console.log('数据库连接成功')
  })
})

使用 conn.connect() 来进行连接,如果没有报错,说明可以成功连接上数据库。

请求结果的 Promise 封装

之前请求到的数据都是直接通过传入的回调函数获取,容易产生回调地狱,mysql2 支持 Promise API,只需要调用 connectionpoolpromise() 函数,然后再继续调用 query()execute(),返回的就是一个 promise 对象,查询结果可以通过 .then() 的方式接收:

const promisePool  = pool.promise()
promisePool
  .execute('SELECT * FROM members WHERE `level` > ?;', [1])
  .then(res => console.log(res))
  .catch(err => console.log(err))

得到的 res 是一个数组,里面包含了 2 个数组,第一个数组对应之前使用回调形式获取结果时的 results,第二个数组对应的是 fields,可以直接在获取结果时进行解构:

.then(([rows, fields]) => {
  console.log(rows)
  console.log(fields)
})

结果如下:

mysql2 的使用及对查询结果的转换

mysql2 的使用及对查询结果的转换 mysql2 的使用及对查询结果的转换

转载自:https://juejin.cn/post/7389913087471992884
评论
请登录