Search K
Appearance
Appearance
📊 SEO元描述:2024年最新Node.js MySQL操作教程,详解mysql2驱动、连接池配置、SQL查询优化。包含完整ORM示例,适合Node.js开发者掌握关系型数据库技术。
核心关键词:Node.js MySQL操作2024、mysql2驱动使用、SQL查询优化、Node.js数据库连接池、关系型数据库开发
长尾关键词:Node.js怎么连接MySQL、mysql2连接池配置、SQL注入防护、Node.js ORM框架、MySQL性能优化
通过本节Node.js MySQL操作教程,你将系统性掌握:
MySQL是什么?这是Web开发中的经典问题。MySQL是世界上最流行的开源关系型数据库管理系统,以其稳定性、性能和易用性而著称,也是企业级应用的首选数据库解决方案。
💡 设计理念:MySQL为结构化数据提供可靠、高效的存储和查询解决方案
让我们从MySQL的连接配置开始:
// 🎉 MySQL连接配置详解
const mysql = require('mysql2/promise');
const mysql2 = require('mysql2');
// 1. 基础连接配置
class MySQLConnection {
constructor() {
this.pool = null;
this.connection = null;
this.config = this.getConfig();
}
// 获取数据库配置
getConfig() {
const env = process.env.NODE_ENV || 'development';
const configs = {
development: {
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'nodeapp_dev',
charset: 'utf8mb4',
timezone: '+08:00',
acquireTimeout: 60000,
timeout: 60000,
reconnect: true
},
test: {
host: process.env.TEST_DB_HOST || 'localhost',
port: process.env.TEST_DB_PORT || 3306,
user: process.env.TEST_DB_USER || 'root',
password: process.env.TEST_DB_PASSWORD || '',
database: process.env.TEST_DB_NAME || 'nodeapp_test',
charset: 'utf8mb4',
timezone: '+08:00'
},
production: {
host: process.env.DB_HOST,
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
charset: 'utf8mb4',
timezone: '+08:00',
ssl: {
rejectUnauthorized: false
},
acquireTimeout: 60000,
timeout: 60000,
reconnect: true
}
};
return configs[env];
}
// 创建连接池(推荐方式)
async createPool() {
try {
const poolConfig = {
...this.config,
waitForConnections: true,
connectionLimit: 10, // 最大连接数
queueLimit: 0, // 队列限制
acquireTimeout: 60000, // 获取连接超时
timeout: 60000, // 查询超时
reconnect: true, // 自动重连
idleTimeout: 300000, // 空闲超时(5分钟)
maxIdle: 10, // 最大空闲连接
enableKeepAlive: true, // 启用保活
keepAliveInitialDelay: 0 // 保活初始延迟
};
this.pool = mysql.createPool(poolConfig);
// 测试连接
const connection = await this.pool.getConnection();
console.log('✅ MySQL连接池创建成功');
console.log(`📍 数据库: ${this.config.database}`);
console.log(`🌐 主机: ${this.config.host}:${this.config.port}`);
connection.release();
// 设置连接池事件监听
this.setupPoolEventListeners();
return this.pool;
} catch (error) {
console.error('❌ MySQL连接池创建失败:', error);
throw error;
}
}
// 创建单个连接
async createConnection() {
try {
this.connection = await mysql.createConnection(this.config);
console.log('✅ MySQL单连接创建成功');
return this.connection;
} catch (error) {
console.error('❌ MySQL连接创建失败:', error);
throw error;
}
}
// 设置连接池事件监听
setupPoolEventListeners() {
if (!this.pool) return;
this.pool.on('connection', (connection) => {
console.log(`🔗 新连接建立: ${connection.threadId}`);
});
this.pool.on('acquire', (connection) => {
console.log(`📥 连接获取: ${connection.threadId}`);
});
this.pool.on('release', (connection) => {
console.log(`📤 连接释放: ${connection.threadId}`);
});
this.pool.on('error', (error) => {
console.error('💥 连接池错误:', error);
});
}
// 获取连接池状态
getPoolStatus() {
if (!this.pool) return null;
return {
totalConnections: this.pool._allConnections.length,
freeConnections: this.pool._freeConnections.length,
acquiringConnections: this.pool._acquiringConnections.length,
queuedRequests: this.pool._connectionQueue.length
};
}
// 执行查询(使用连接池)
async query(sql, params = []) {
try {
if (!this.pool) {
await this.createPool();
}
const [rows, fields] = await this.pool.execute(sql, params);
return { rows, fields };
} catch (error) {
console.error('❌ 查询执行失败:', error);
throw error;
}
}
// 执行事务
async transaction(callback) {
const connection = await this.pool.getConnection();
try {
await connection.beginTransaction();
const result = await callback(connection);
await connection.commit();
console.log('✅ 事务提交成功');
return result;
} catch (error) {
await connection.rollback();
console.log('🔄 事务回滚');
throw error;
} finally {
connection.release();
}
}
// 关闭连接
async close() {
try {
if (this.pool) {
await this.pool.end();
console.log('🔒 MySQL连接池已关闭');
}
if (this.connection) {
await this.connection.end();
console.log('🔒 MySQL连接已关闭');
}
} catch (error) {
console.error('❌ 关闭连接时出错:', error);
}
}
// 健康检查
async healthCheck() {
try {
const { rows } = await this.query('SELECT 1 as health');
return rows[0].health === 1;
} catch (error) {
console.error('❌ 数据库健康检查失败:', error);
return false;
}
}
}
// 使用示例
const dbConnection = new MySQLConnection();
// 初始化数据库连接
async function initializeDatabase() {
try {
await dbConnection.createPool();
// 健康检查
const isHealthy = await dbConnection.healthCheck();
console.log('🏥 数据库健康状态:', isHealthy ? '正常' : '异常');
// 连接池状态
const poolStatus = dbConnection.getPoolStatus();
console.log('📊 连接池状态:', poolStatus);
} catch (error) {
console.error('💥 数据库初始化失败:', error);
process.exit(1);
}
}
// 优雅关闭
process.on('SIGINT', async () => {
console.log('🛑 正在关闭数据库连接...');
await dbConnection.close();
process.exit(0);
});
module.exports = {
MySQLConnection,
dbConnection,
initializeDatabase
};
console.log('MySQL连接配置完成');// 🚀 MySQL CRUD操作详解
const { dbConnection } = require('./mysql-connection');
class UserRepository {
constructor() {
this.db = dbConnection;
this.tableName = 'users';
}
// 1. 创建用户表
async createTable() {
const sql = `
CREATE TABLE IF NOT EXISTS ${this.tableName} (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
avatar VARCHAR(255),
bio TEXT,
date_of_birth DATE,
role ENUM('user', 'admin', 'moderator') DEFAULT 'user',
is_active BOOLEAN DEFAULT TRUE,
email_verified BOOLEAN DEFAULT FALSE,
last_login TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_username (username),
INDEX idx_email (email),
INDEX idx_role (role),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`;
try {
await this.db.query(sql);
console.log('✅ 用户表创建成功');
} catch (error) {
console.error('❌ 用户表创建失败:', error);
throw error;
}
}
// 2. 创建用户 (Create)
async createUser(userData) {
const sql = `
INSERT INTO ${this.tableName}
(username, email, password, first_name, last_name, avatar, bio, date_of_birth, role)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
const params = [
userData.username,
userData.email,
userData.password,
userData.firstName || null,
userData.lastName || null,
userData.avatar || null,
userData.bio || null,
userData.dateOfBirth || null,
userData.role || 'user'
];
try {
const { rows } = await this.db.query(sql, params);
const userId = rows.insertId;
console.log('✅ 用户创建成功, ID:', userId);
return await this.getUserById(userId);
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
throw new Error('用户名或邮箱已存在');
}
throw error;
}
}
// 3. 读取用户 (Read)
async getUserById(userId) {
const sql = `
SELECT id, username, email, first_name, last_name, avatar, bio,
date_of_birth, role, is_active, email_verified, last_login,
created_at, updated_at
FROM ${this.tableName}
WHERE id = ? AND is_active = TRUE
`;
try {
const { rows } = await this.db.query(sql, [userId]);
if (rows.length === 0) {
throw new Error('用户不存在');
}
return this.formatUser(rows[0]);
} catch (error) {
throw error;
}
}
async getUserByEmail(email) {
const sql = `
SELECT id, username, email, password, first_name, last_name, avatar, bio,
date_of_birth, role, is_active, email_verified, last_login,
created_at, updated_at
FROM ${this.tableName}
WHERE email = ? AND is_active = TRUE
`;
try {
const { rows } = await this.db.query(sql, [email]);
if (rows.length === 0) {
return null;
}
return this.formatUser(rows[0]);
} catch (error) {
throw error;
}
}
async getUsers(options = {}) {
const {
page = 1,
limit = 10,
sortBy = 'created_at',
sortOrder = 'DESC',
search = '',
role = null,
isActive = true
} = options;
// 构建WHERE条件
let whereConditions = [];
let params = [];
if (isActive !== null) {
whereConditions.push('is_active = ?');
params.push(isActive);
}
if (role) {
whereConditions.push('role = ?');
params.push(role);
}
if (search) {
whereConditions.push('(username LIKE ? OR email LIKE ? OR first_name LIKE ? OR last_name LIKE ?)');
const searchPattern = `%${search}%`;
params.push(searchPattern, searchPattern, searchPattern, searchPattern);
}
const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
// 计算偏移量
const offset = (page - 1) * limit;
// 查询用户列表
const usersSql = `
SELECT id, username, email, first_name, last_name, avatar, bio,
date_of_birth, role, is_active, email_verified, last_login,
created_at, updated_at
FROM ${this.tableName}
${whereClause}
ORDER BY ${sortBy} ${sortOrder}
LIMIT ? OFFSET ?
`;
// 查询总数
const countSql = `
SELECT COUNT(*) as total
FROM ${this.tableName}
${whereClause}
`;
try {
const [usersResult, countResult] = await Promise.all([
this.db.query(usersSql, [...params, limit, offset]),
this.db.query(countSql, params)
]);
const users = usersResult.rows.map(user => this.formatUser(user));
const total = countResult.rows[0].total;
return {
users,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit),
hasNext: page * limit < total,
hasPrev: page > 1
}
};
} catch (error) {
throw error;
}
}
// 4. 更新用户 (Update)
async updateUser(userId, updateData) {
// 构建动态更新SQL
const allowedFields = [
'username', 'email', 'first_name', 'last_name',
'avatar', 'bio', 'date_of_birth', 'role', 'is_active', 'email_verified'
];
const updateFields = [];
const params = [];
Object.keys(updateData).forEach(key => {
if (allowedFields.includes(key) && updateData[key] !== undefined) {
updateFields.push(`${key} = ?`);
params.push(updateData[key]);
}
});
if (updateFields.length === 0) {
throw new Error('没有有效的更新字段');
}
params.push(userId);
const sql = `
UPDATE ${this.tableName}
SET ${updateFields.join(', ')}, updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND is_active = TRUE
`;
try {
const { rows } = await this.db.query(sql, params);
if (rows.affectedRows === 0) {
throw new Error('用户不存在或更新失败');
}
console.log('✅ 用户更新成功, ID:', userId);
return await this.getUserById(userId);
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
throw new Error('用户名或邮箱已被其他用户使用');
}
throw error;
}
}
// 5. 删除用户 (Delete)
async deleteUser(userId) {
const sql = `DELETE FROM ${this.tableName} WHERE id = ?`;
try {
const { rows } = await this.db.query(sql, [userId]);
if (rows.affectedRows === 0) {
throw new Error('用户不存在');
}
console.log('✅ 用户删除成功, ID:', userId);
return { id: userId, deleted: true };
} catch (error) {
throw error;
}
}
// 软删除
async softDeleteUser(userId) {
const sql = `
UPDATE ${this.tableName}
SET is_active = FALSE, updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND is_active = TRUE
`;
try {
const { rows } = await this.db.query(sql, [userId]);
if (rows.affectedRows === 0) {
throw new Error('用户不存在');
}
console.log('✅ 用户软删除成功, ID:', userId);
return { id: userId, softDeleted: true };
} catch (error) {
throw error;
}
}
// 工具方法:格式化用户数据
formatUser(user) {
return {
id: user.id,
username: user.username,
email: user.email,
firstName: user.first_name,
lastName: user.last_name,
fullName: user.first_name && user.last_name
? `${user.first_name} ${user.last_name}`
: user.username,
avatar: user.avatar,
bio: user.bio,
dateOfBirth: user.date_of_birth,
role: user.role,
isActive: Boolean(user.is_active),
emailVerified: Boolean(user.email_verified),
lastLogin: user.last_login,
createdAt: user.created_at,
updatedAt: user.updated_at
};
}
}
module.exports = UserRepository;
console.log('MySQL CRUD操作完成');SQL操作要点:
// 🔐 MySQL事务处理详解
const { dbConnection } = require('./mysql-connection');
class TransactionService {
constructor() {
this.db = dbConnection;
}
// 1. 基础事务操作
async basicTransaction() {
return await this.db.transaction(async (connection) => {
// 在事务中执行多个操作
const [result1] = await connection.execute(
'INSERT INTO users (username, email, password) VALUES (?, ?, ?)',
['testuser', 'test@example.com', 'hashedpassword']
);
const userId = result1.insertId;
await connection.execute(
'INSERT INTO user_profiles (user_id, first_name, last_name) VALUES (?, ?, ?)',
[userId, 'Test', 'User']
);
return { userId, message: '用户和档案创建成功' };
});
}
// 2. 复杂业务事务 - 转账示例
async transferMoney(fromUserId, toUserId, amount) {
if (amount <= 0) {
throw new Error('转账金额必须大于0');
}
return await this.db.transaction(async (connection) => {
// 1. 检查发送方余额
const [fromUserRows] = await connection.execute(
'SELECT id, balance FROM user_accounts WHERE user_id = ? FOR UPDATE',
[fromUserId]
);
if (fromUserRows.length === 0) {
throw new Error('发送方账户不存在');
}
const fromAccount = fromUserRows[0];
if (fromAccount.balance < amount) {
throw new Error('余额不足');
}
// 2. 检查接收方账户
const [toUserRows] = await connection.execute(
'SELECT id, balance FROM user_accounts WHERE user_id = ? FOR UPDATE',
[toUserId]
);
if (toUserRows.length === 0) {
throw new Error('接收方账户不存在');
}
const toAccount = toUserRows[0];
// 3. 更新发送方余额
await connection.execute(
'UPDATE user_accounts SET balance = balance - ?, updated_at = NOW() WHERE user_id = ?',
[amount, fromUserId]
);
// 4. 更新接收方余额
await connection.execute(
'UPDATE user_accounts SET balance = balance + ?, updated_at = NOW() WHERE user_id = ?',
[amount, toUserId]
);
// 5. 记录转账历史
const [transferResult] = await connection.execute(
`INSERT INTO transfer_history
(from_user_id, to_user_id, amount, status, created_at)
VALUES (?, ?, ?, 'completed', NOW())`,
[fromUserId, toUserId, amount]
);
const transferId = transferResult.insertId;
// 6. 记录账户变动日志
await connection.execute(
`INSERT INTO account_logs
(user_id, transfer_id, type, amount, balance_before, balance_after, created_at)
VALUES (?, ?, 'debit', ?, ?, ?, NOW())`,
[fromUserId, transferId, amount, fromAccount.balance, fromAccount.balance - amount]
);
await connection.execute(
`INSERT INTO account_logs
(user_id, transfer_id, type, amount, balance_before, balance_after, created_at)
VALUES (?, ?, 'credit', ?, ?, ?, NOW())`,
[toUserId, transferId, amount, toAccount.balance, toAccount.balance + amount]
);
console.log(`✅ 转账成功: ${fromUserId} -> ${toUserId}, 金额: ${amount}`);
return {
transferId,
fromUserId,
toUserId,
amount,
status: 'completed',
timestamp: new Date()
};
});
}
// 3. 批量操作事务
async batchCreateUsers(usersData) {
return await this.db.transaction(async (connection) => {
const createdUsers = [];
const errors = [];
for (let i = 0; i < usersData.length; i++) {
try {
const userData = usersData[i];
// 检查用户是否已存在
const [existingUsers] = await connection.execute(
'SELECT id FROM users WHERE username = ? OR email = ?',
[userData.username, userData.email]
);
if (existingUsers.length > 0) {
errors.push({
index: i,
data: userData,
error: '用户名或邮箱已存在'
});
continue;
}
// 创建用户
const [userResult] = await connection.execute(
`INSERT INTO users (username, email, password, role, created_at)
VALUES (?, ?, ?, ?, NOW())`,
[userData.username, userData.email, userData.password, userData.role || 'user']
);
const userId = userResult.insertId;
// 创建用户档案
await connection.execute(
`INSERT INTO user_profiles (user_id, first_name, last_name, created_at)
VALUES (?, ?, ?, NOW())`,
[userId, userData.firstName || '', userData.lastName || '']
);
// 创建用户账户
await connection.execute(
`INSERT INTO user_accounts (user_id, balance, created_at)
VALUES (?, 0.00, NOW())`,
[userId]
);
createdUsers.push({
id: userId,
username: userData.username,
email: userData.email
});
} catch (error) {
errors.push({
index: i,
data: usersData[i],
error: error.message
});
}
}
// 如果有错误且要求全部成功,则回滚
if (errors.length > 0 && process.env.BATCH_STRICT_MODE === 'true') {
throw new Error(`批量创建失败,错误数量: ${errors.length}`);
}
console.log(`✅ 批量创建用户完成: 成功${createdUsers.length}个,失败${errors.length}个`);
return {
success: createdUsers,
errors: errors,
total: usersData.length,
successCount: createdUsers.length,
errorCount: errors.length
};
});
}
// 4. 事务隔离级别设置
async setTransactionIsolationLevel(level = 'READ COMMITTED') {
const validLevels = [
'READ UNCOMMITTED',
'READ COMMITTED',
'REPEATABLE READ',
'SERIALIZABLE'
];
if (!validLevels.includes(level)) {
throw new Error(`无效的隔离级别: ${level}`);
}
try {
await this.db.query(`SET SESSION TRANSACTION ISOLATION LEVEL ${level}`);
console.log(`✅ 事务隔离级别设置为: ${level}`);
} catch (error) {
console.error('❌ 设置事务隔离级别失败:', error);
throw error;
}
}
// 5. 手动事务控制
async manualTransactionControl() {
const connection = await this.db.pool.getConnection();
try {
// 开始事务
await connection.beginTransaction();
console.log('🔄 事务开始');
// 执行操作1
const [result1] = await connection.execute(
'INSERT INTO users (username, email, password) VALUES (?, ?, ?)',
['manual_user', 'manual@example.com', 'password']
);
const userId = result1.insertId;
console.log('✅ 用户创建成功, ID:', userId);
// 模拟业务逻辑检查
if (userId % 2 === 0) {
// 偶数ID,继续执行
await connection.execute(
'INSERT INTO user_profiles (user_id, first_name) VALUES (?, ?)',
[userId, 'Manual User']
);
console.log('✅ 用户档案创建成功');
// 提交事务
await connection.commit();
console.log('✅ 事务提交成功');
return { userId, status: 'committed' };
} else {
// 奇数ID,回滚事务
await connection.rollback();
console.log('🔄 事务回滚');
return { userId, status: 'rolled_back', reason: '业务规则不满足' };
}
} catch (error) {
// 发生错误,回滚事务
await connection.rollback();
console.log('🔄 事务回滚(错误)');
throw error;
} finally {
// 释放连接
connection.release();
}
}
// 6. 事务性能监控
async monitoredTransaction(callback) {
const startTime = Date.now();
let transactionId = null;
try {
const result = await this.db.transaction(async (connection) => {
// 获取事务ID(MySQL 8.0+)
try {
const [txnRows] = await connection.execute('SELECT CONNECTION_ID() as id');
transactionId = txnRows[0].id;
} catch (e) {
// 忽略获取事务ID的错误
}
return await callback(connection);
});
const duration = Date.now() - startTime;
console.log(`📊 事务执行完成 - ID: ${transactionId}, 耗时: ${duration}ms`);
return result;
} catch (error) {
const duration = Date.now() - startTime;
console.log(`📊 事务执行失败 - ID: ${transactionId}, 耗时: ${duration}ms, 错误: ${error.message}`);
throw error;
}
}
// 7. 死锁检测和重试
async transactionWithRetry(callback, maxRetries = 3) {
let attempt = 0;
while (attempt < maxRetries) {
try {
return await this.db.transaction(callback);
} catch (error) {
attempt++;
// 检查是否是死锁错误
if (error.code === 'ER_LOCK_DEADLOCK' && attempt < maxRetries) {
console.log(`⚠️ 检测到死锁,第${attempt}次重试...`);
// 随机延迟后重试
const delay = Math.random() * 100 + 50; // 50-150ms
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
}
module.exports = TransactionService;
console.log('MySQL事务处理服务完成');事务处理要点:
// 💎 Sequelize ORM详解
const { Sequelize, DataTypes, Op } = require('sequelize');
const bcrypt = require('bcrypt');
// 1. Sequelize配置和连接
class SequelizeConfig {
constructor() {
this.sequelize = null;
this.models = {};
}
async initialize() {
const config = {
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 3306,
database: process.env.DB_NAME || 'nodeapp',
username: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
dialect: 'mysql',
dialectOptions: {
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
supportBigNumbers: true,
bigNumberStrings: true
},
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000
},
logging: process.env.NODE_ENV === 'development' ? console.log : false,
timezone: '+08:00'
};
this.sequelize = new Sequelize(config);
try {
await this.sequelize.authenticate();
console.log('✅ Sequelize数据库连接成功');
} catch (error) {
console.error('❌ Sequelize连接失败:', error);
throw error;
}
// 定义模型
this.defineModels();
// 建立关联
this.setupAssociations();
return this.sequelize;
}
// 2. 定义用户模型
defineModels() {
// 用户模型
this.models.User = this.sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
validate: {
len: [3, 50],
isAlphanumeric: true
}
},
email: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING(255),
allowNull: false,
validate: {
len: [6, 255]
}
},
firstName: {
type: DataTypes.STRING(50),
field: 'first_name'
},
lastName: {
type: DataTypes.STRING(50),
field: 'last_name'
},
avatar: {
type: DataTypes.STRING(255)
},
bio: {
type: DataTypes.TEXT
},
dateOfBirth: {
type: DataTypes.DATEONLY,
field: 'date_of_birth',
validate: {
isBefore: new Date().toISOString().split('T')[0]
}
},
role: {
type: DataTypes.ENUM('user', 'admin', 'moderator'),
defaultValue: 'user'
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
field: 'is_active'
},
emailVerified: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'email_verified'
},
lastLogin: {
type: DataTypes.DATE,
field: 'last_login'
}
}, {
tableName: 'users',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
paranoid: true, // 软删除
deletedAt: 'deleted_at',
indexes: [
{ fields: ['username'] },
{ fields: ['email'] },
{ fields: ['role'] },
{ fields: ['created_at'] }
],
hooks: {
beforeCreate: async (user) => {
if (user.password) {
user.password = await bcrypt.hash(user.password, 12);
}
},
beforeUpdate: async (user) => {
if (user.changed('password')) {
user.password = await bcrypt.hash(user.password, 12);
}
}
}
});
// 用户档案模型
this.models.UserProfile = this.sequelize.define('UserProfile', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'user_id'
},
phone: {
type: DataTypes.STRING(20),
validate: {
is: /^[+]?[\d\s-()]+$/
}
},
address: {
type: DataTypes.TEXT
},
city: {
type: DataTypes.STRING(50)
},
country: {
type: DataTypes.STRING(50)
},
website: {
type: DataTypes.STRING(255),
validate: {
isUrl: true
}
},
socialLinks: {
type: DataTypes.JSON,
field: 'social_links'
}
}, {
tableName: 'user_profiles',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
// 文章模型
this.models.Post = this.sequelize.define('Post', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
title: {
type: DataTypes.STRING(200),
allowNull: false,
validate: {
len: [5, 200]
}
},
slug: {
type: DataTypes.STRING(250),
allowNull: false,
unique: true
},
content: {
type: DataTypes.TEXT('long'),
allowNull: false
},
excerpt: {
type: DataTypes.TEXT
},
authorId: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'author_id'
},
status: {
type: DataTypes.ENUM('draft', 'published', 'archived'),
defaultValue: 'draft'
},
publishedAt: {
type: DataTypes.DATE,
field: 'published_at'
},
viewCount: {
type: DataTypes.INTEGER,
defaultValue: 0,
field: 'view_count'
},
tags: {
type: DataTypes.JSON
}
}, {
tableName: 'posts',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
paranoid: true,
deletedAt: 'deleted_at',
indexes: [
{ fields: ['author_id'] },
{ fields: ['status'] },
{ fields: ['published_at'] },
{ fields: ['slug'], unique: true }
]
});
// 添加实例方法
this.models.User.prototype.comparePassword = async function(candidatePassword) {
return await bcrypt.compare(candidatePassword, this.password);
};
this.models.User.prototype.getFullName = function() {
return this.firstName && this.lastName
? `${this.firstName} ${this.lastName}`
: this.username;
};
// 添加类方法
this.models.User.findByEmail = function(email) {
return this.findOne({ where: { email } });
};
this.models.User.getActiveUsers = function() {
return this.findAll({ where: { isActive: true } });
};
}
// 3. 建立模型关联
setupAssociations() {
const { User, UserProfile, Post } = this.models;
// 用户和档案:一对一关系
User.hasOne(UserProfile, {
foreignKey: 'userId',
as: 'profile',
onDelete: 'CASCADE'
});
UserProfile.belongsTo(User, {
foreignKey: 'userId',
as: 'user'
});
// 用户和文章:一对多关系
User.hasMany(Post, {
foreignKey: 'authorId',
as: 'posts',
onDelete: 'CASCADE'
});
Post.belongsTo(User, {
foreignKey: 'authorId',
as: 'author'
});
}
// 4. 同步数据库
async syncDatabase(force = false) {
try {
await this.sequelize.sync({ force, alter: !force });
console.log('✅ 数据库同步完成');
} catch (error) {
console.error('❌ 数据库同步失败:', error);
throw error;
}
}
// 5. 关闭连接
async close() {
if (this.sequelize) {
await this.sequelize.close();
console.log('🔒 Sequelize连接已关闭');
}
}
}
// 6. Sequelize服务类
class SequelizeUserService {
constructor(sequelize) {
this.User = sequelize.models.User;
this.UserProfile = sequelize.models.UserProfile;
this.Post = sequelize.models.Post;
}
// 创建用户(带档案)
async createUserWithProfile(userData, profileData = {}) {
const transaction = await this.User.sequelize.transaction();
try {
// 创建用户
const user = await this.User.create(userData, { transaction });
// 创建用户档案
if (Object.keys(profileData).length > 0) {
await this.UserProfile.create({
userId: user.id,
...profileData
}, { transaction });
}
await transaction.commit();
// 返回包含档案的用户信息
return await this.getUserWithProfile(user.id);
} catch (error) {
await transaction.rollback();
throw error;
}
}
// 获取用户(包含档案和文章)
async getUserWithProfile(userId) {
return await this.User.findByPk(userId, {
include: [
{
model: this.UserProfile,
as: 'profile'
},
{
model: this.Post,
as: 'posts',
where: { status: 'published' },
required: false,
limit: 5,
order: [['publishedAt', 'DESC']]
}
]
});
}
// 复杂查询示例
async searchUsers(searchOptions) {
const {
keyword,
role,
isActive,
page = 1,
limit = 10,
sortBy = 'createdAt',
sortOrder = 'DESC'
} = searchOptions;
const whereClause = {};
if (keyword) {
whereClause[Op.or] = [
{ username: { [Op.like]: `%${keyword}%` } },
{ email: { [Op.like]: `%${keyword}%` } },
{ firstName: { [Op.like]: `%${keyword}%` } },
{ lastName: { [Op.like]: `%${keyword}%` } }
];
}
if (role) {
whereClause.role = role;
}
if (isActive !== undefined) {
whereClause.isActive = isActive;
}
const offset = (page - 1) * limit;
const { count, rows } = await this.User.findAndCountAll({
where: whereClause,
include: [
{
model: this.UserProfile,
as: 'profile',
required: false
}
],
order: [[sortBy, sortOrder]],
limit,
offset,
distinct: true
});
return {
users: rows,
pagination: {
page,
limit,
total: count,
pages: Math.ceil(count / limit)
}
};
}
// 聚合查询示例
async getUserStatistics() {
const stats = await this.User.findAll({
attributes: [
'role',
[this.User.sequelize.fn('COUNT', this.User.sequelize.col('id')), 'count'],
[this.User.sequelize.fn('COUNT', this.User.sequelize.literal('CASE WHEN is_active = 1 THEN 1 END')), 'activeCount']
],
group: ['role'],
raw: true
});
return stats;
}
}
// 使用示例
const sequelizeConfig = new SequelizeConfig();
async function initializeSequelize() {
try {
await sequelizeConfig.initialize();
await sequelizeConfig.syncDatabase();
const userService = new SequelizeUserService(sequelizeConfig);
console.log('✅ Sequelize初始化完成');
return { sequelize: sequelizeConfig.sequelize, userService };
} catch (error) {
console.error('❌ Sequelize初始化失败:', error);
throw error;
}
}
module.exports = {
SequelizeConfig,
SequelizeUserService,
initializeSequelize
};
console.log('Sequelize ORM配置完成');Sequelize ORM要点:
通过本节Node.js MySQL操作教程的学习,你已经掌握:
A: 选择依据:1)复杂查询和性能要求高时使用原生SQL;2)快速开发和维护性要求高时使用ORM;3)团队技能水平和项目复杂度;4)可以混合使用,在ORM中执行原生SQL。
A: 防护措施:1)始终使用参数化查询;2)验证和清理用户输入;3)使用ORM框架的内置防护;4)限制数据库用户权限;5)定期安全审计;6)使用Web应用防火墙。
A: 配置原则:1)根据并发量设置连接数;2)设置合理的超时时间;3)启用连接保活机制;4)监控连接池状态;5)考虑数据库服务器性能;6)测试不同配置的性能表现。
A: 优化策略:1)创建合适的索引;2)避免SELECT *;3)使用LIMIT分页;4)优化WHERE条件;5)使用EXPLAIN分析查询;6)避免子查询,使用JOIN;7)合理使用缓存。
A: 使用场景:1)多表操作需要保证一致性;2)金融交易等关键业务;3)批量操作需要原子性;4)数据完整性要求高的场景;5)需要回滚机制的操作。
"MySQL作为成熟的关系型数据库,为企业级应用提供了可靠的数据存储解决方案。掌握了MySQL操作技术,你就具备了处理复杂业务数据的重要能力。继续学习数据模型设计,提升你的数据库架构技能!"