Node.js连接数据库封面

引言:Node.js 与数据库的第一次握手

2013 年,Node.js 在国内开发者社区刚刚兴起。那是一个令人兴奋的年代 —— JavaScript 不再局限于浏览器,它开始进军服务端,开始能够处理真正的业务逻辑。而我面临的第一个挑战就是:如何用 Node.js 连接数据库?

对于习惯了 Java 和 JDBC 的我来说,Node.js 的异步模型是一个全新的思维模式。传统的同步编程中,我们写下一行代码,等待它执行完毕,再执行下一行。但在 Node.js 的世界里,一切都是非阻塞的,通过回调函数来处理异步结果。这种编程范式的转变,在学习数据库连接的过程中体现得淋漓尽致。

这篇笔记记录了我当时学习 Node.js 连接 MySQL 数据库的全过程,包括新旧两个版本的不同连接方式。虽然这些代码在今天看来已经有些过时,但理解其中的设计思路,对于学习现代 Node.js 数据库操作仍然大有裨益。


前置准备:安装 MySQL 驱动

无论新旧版本,连接数据库的第一步都是安装对应的驱动程序。在 Node.js 中,驱动管理通过 npm(Node Package Manager)来完成。

1
npm install mysql

这个命令会在当前项目目录下安装 mysql 模块,类似于 Java 中的 JDBC 驱动。安装成功后,驱动文件的位置取决于你的操作系统和安装方式:

  • WindowsC:\Users\用户名\node_modules\mysql
  • Linux/opt/mysql/node_modules/mysql(具体路径可根据实际情况设置)

关于驱动的详细使用说明,可以参考官方文档或通过以下链接查看 README:
http://daringfireball.net/projects/markdown/dingus


方式一:旧版本连接方式

旧版本的 mysql 模块使用的是 createClient() 方法来创建数据库连接。这种方式相对简单直观,适合初学者快速上手。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
var mysql = require('mysql'); // mysql 与项目在同一目录下

var TEST_DATABASE = 'nodejs_db';
var TEST_TABLE = 'test';

// 创建连接
var client = mysql.createClient({
user: 'root',
password: 'rainbow',
});

// 创建数据库
client.query('CREATE DATABASE ' + TEST_DATABASE, function(err) {
if (err && err.number != mysql.ERROR_DB_CREATE_EXISTS) {
throw err;
}
});

// 不指定回调函数时,如果出错会体现为客户端错误
client.query('USE ' + TEST_DATABASE);

// 创建表格
client.query(
'CREATE TABLE ' + TEST_TABLE +
'(id INT(11) AUTO_INCREMENT, ' +
'name VARCHAR(255), ' +
'PRIMARY KEY (id))'
);

// 插入数据
client.query(
'INSERT INTO ' + TEST_TABLE + ' ' +
'SET name = ?',
['nodejs1']
);

var query = client.query(
'INSERT INTO ' + TEST_TABLE + ' ' +
'SET name = ?',
['nodejs2']
);

// 查询并设置回调函数
client.query(
'SELECT * FROM ' + TEST_TABLE,
function selectCb(err, results, fields) {
if (err) {
throw err;
}
console.log(results);
console.log(fields);
client.end();
}
);

代码解读

1. 连接创建createClient() 方法创建一个数据库客户端对象,传入用户名和密码即可完成连接配置。

2. 数据库操作链:旧版本的一个显著特点是,许多数据库操作可以链式调用,不需要每次都指定回调函数。这种方式写起来很流畅,但在错误处理上不够精细。

3. 参数化查询:注意 SET name = ? 中的问号,这是参数化查询的占位符。实际值通过第二个参数数组传入。这种写法可以有效防止 SQL 注入攻击,是数据库编程的最佳实践。

4. 回调函数selectCb 回调函数接收三个参数:错误对象、查询结果、字段信息。这是 Node.js 经典的错误优先回调模式(error-first callback pattern)。

Node.js旧版数据库连接方式


方式二:新版本连接方式

随着 Node.js 生态的发展,mysql 模块也进行了重大改版。新版本使用 createConnection() 方法来创建连接,整体架构更加面向对象,更适合企业级应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
var mysql = require('/usr/local/lib/node_modules/mysql'); // mysql 驱动安装路径

var connection = mysql.createConnection({
host: 'localhost',
port: 3306,
user: 'root',
password: 'mysql',
database: 'testMysql',
charset: 'UTF8_GENERAL_CI',
debug: false
});

console.log('Connection to Mysql');

http = require("http");

var server = http.createServer(function(request, response) {
response.writeHeader(200, {"Content-Type": "text/html"});

connection.query('USE testMysql');

connection.query('SELECT * FROM myTable', function(err, results, fields) {
if (err) {
throw err;
}

var data = '<html><head></head><body><div align="center"><h1>TestMysql</h1>' +
'<table border="1" style="text-align:center;"><tr>' +
'<th style="width:150px;">Id</th><th style="width:150px;">firstName</th>' +
'<th style="width:150px;">LastName</th><th style="width:150px;">Message</th></tr>';

for (var i = 0; i < results.length; i++) {
var firstResult = results[i];
data += '<tr><td>' + firstResult['id'] + '</td>' +
'<td>' + firstResult['firstname'] + '</td>' +
'<td>' + firstResult['lastname'] + '</td>' +
'<td>' + firstResult['message'] + '</td>' +
'</tr>';
}
data += '</table></div></body></html>';
response.write(data);
response.end();
});
});

server.listen(8888);

var sys = require("util");
sys.puts("Server running at http://localhost:8888/");

代码解读

1. 连接配置更丰富:新版本在 createConnection() 中可以指定更多参数,包括主机地址、端口、数据库名、字符集和调试开关等。这种显式配置方式让连接参数一目了然,更便于维护。

2. 面向连接的设计createConnection() 返回一个连接对象,所有数据库操作都通过该连接对象执行。这与传统 JDBC 的 Connection 模式非常相似,对于有 Java 背景的开发者来说更加亲切。

3. 与 HTTP 服务器的集成:这个示例完整展示了一个 Node.js HTTP 服务器如何与数据库协同工作。每次收到 HTTP 请求时,执行数据库查询并将结果渲染为 HTML 表格返回给浏览器。

4. 字符集配置UTF8_GENERAL_CI 确保了中文数据的正确处理。在实际项目中,字符集配置不当是乱码问题的常见根源。


两种方式的对比分析

对比维度 旧版本 createClient 新版本 createConnection
学习难度 较低,适合初学者 中等,需要理解连接概念
代码风格 简洁直接 面向对象,结构清晰
配置灵活性 有限 丰富,支持更多选项
错误处理 隐式,依赖客户端错误 显式,每个操作可指定回调
适用场景 快速原型开发 企业级应用
可维护性 一般 良好

我的看法:旧版本的方式确实更加通俗易懂,代码量少,逻辑直观,很适合初学者快速上手和理解 Node.js 的异步编程模型。但新版本的方式更加接近面向对象的设计理念,连接配置集中管理,错误处理更加精细,这是未来技术发展的一种趋势,也是更适合企业级开发的方式。

Node.js新旧版本对比


踩坑经验

在学习过程中,我遇到了不少问题,这里总结几个常见的坑:

坑 1:版本兼容性

Node.js 的版本更新非常快,不同版本的 Node.js 对 mysql 模块的支持可能不同。如果遇到 createClient is not a function 这样的错误,很可能是因为 mysql 模块已经升级到了新版本,旧 API 已被废弃。

解决方案:查看 mysql 模块的文档,确认当前版本支持的 API。必要时可以锁定 mysql 模块的版本号:npm install mysql@2.0.0

坑 2:异步操作的顺序问题

在旧版本中,由于许多操作不需要回调函数,很容易产生”代码执行顺序”的错觉。实际上,Node.js 的异步特性意味着代码的书写顺序不等于执行顺序。

解决方案:始终为关键操作指定回调函数,确保在适当的时机执行后续操作。

坑 3:连接泄漏

如果没有正确关闭数据库连接,会导致连接池耗尽。在旧版本中,使用 client.end() 来关闭连接;在新版本中,使用 connection.end() 或在不需要时及时释放连接。


写在后面:Node.js 数据库连接的演进

回顾 2013 年的这篇笔记,Node.js 的数据库连接方式已经发生了巨大的变化。如今,我们有了更多现代化的选择:

  • Promise/async-await:取代回调函数,让异步代码更加清晰
  • 连接池:自动管理数据库连接的生命周期
  • ORM/ODM:如 Sequelize、Prisma 等,让数据库操作更加面向对象
  • TypeScript 支持:类型安全的数据库操作

虽然技术在不断进步,但核心的设计思想和学习路径是相通的。从简单的连接到复杂的连接管理,从回调到 Promise,每一步都是为了一个目标:让代码更易读、更易维护、更可靠。

这篇笔记写于 2013 年,是我初学 Node.js 时的学习记录。如今回看,代码风格虽已陈旧,但那种对新技术的好奇和热情,至今未减。技术的浪潮滚滚向前,唯有持续学习,方能跟上时代的步伐。