Skip to content

Node.js MySQL操作2024:关系型数据库开发完整指南

📊 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性能优化


📚 MySQL操作学习目标与核心收获

通过本节Node.js MySQL操作教程,你将系统性掌握:

  • MySQL连接配置:掌握mysql2驱动的安装、配置和连接池管理
  • SQL查询操作:学会执行各种SQL语句和参数化查询
  • 事务处理:理解数据库事务的概念和在Node.js中的实现
  • ORM框架使用:掌握Sequelize等ORM框架的使用和最佳实践
  • 查询优化技巧:学会SQL查询优化和性能调优方法
  • 安全最佳实践:了解SQL注入防护和数据库安全措施

🎯 适合人群

  • Node.js进阶学习者的关系型数据库技能提升
  • 后端开发工程师的SQL数据库操作掌握
  • 全栈开发者的数据持久化技术学习
  • 数据库开发者的Node.js集成实战

🌟 MySQL是什么?为什么选择关系型数据库?

MySQL是什么?这是Web开发中的经典问题。MySQL是世界上最流行的开源关系型数据库管理系统,以其稳定性、性能和易用性而著称,也是企业级应用的首选数据库解决方案。

MySQL的核心优势

  • 🎯 ACID特性:保证数据的原子性、一致性、隔离性和持久性
  • 🔧 成熟稳定:经过数十年发展,技术成熟,社区活跃
  • 💡 标准SQL:支持标准SQL语法,学习成本低
  • 📚 丰富生态:拥有完善的工具链和第三方支持
  • 🚀 高性能:优秀的查询优化器和存储引擎

💡 设计理念:MySQL为结构化数据提供可靠、高效的存储和查询解决方案

MySQL连接配置

让我们从MySQL的连接配置开始:

javascript
// 🎉 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连接要点

  • 连接池:使用连接池提高并发性能和资源利用率
  • 配置管理:区分不同环境的数据库配置
  • 错误处理:实现完善的连接错误处理和重连机制
  • 监控指标:监控连接池状态和数据库健康状况

SQL查询操作详解

基础CRUD操作实现

javascript
// 🚀 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操作要点

  • 🎯 参数化查询:使用占位符防止SQL注入攻击
  • 🎯 动态SQL:根据条件动态构建查询语句
  • 🎯 数据验证:在数据库层面和应用层面都进行验证
  • 🎯 错误处理:区分不同类型的数据库错误
  • 🎯 性能优化:使用索引和合理的查询结构

事务处理详解

数据库事务的实现和最佳实践

javascript
// 🔐 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事务处理服务完成');

事务处理要点

  • 🎯 ACID特性:确保事务的原子性、一致性、隔离性和持久性
  • 🎯 锁机制:合理使用行锁和表锁,避免死锁
  • 🎯 隔离级别:根据业务需求选择合适的事务隔离级别
  • 🎯 错误处理:实现完善的事务回滚和错误恢复机制
  • 🎯 性能监控:监控事务执行时间和资源使用情况

Sequelize ORM框架使用

现代化的数据库操作方式

javascript
// 💎 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要点

  • 🎯 模型定义:使用DataTypes定义字段类型和验证规则
  • 🎯 关联关系:建立模型间的一对一、一对多、多对多关系
  • 🎯 钩子函数:在数据操作前后执行自定义逻辑
  • 🎯 查询构建:使用链式API构建复杂查询
  • 🎯 事务支持:内置事务支持,确保数据一致性

📚 MySQL操作学习总结与下一步规划

✅ 本节核心收获回顾

通过本节Node.js MySQL操作教程的学习,你已经掌握:

  1. MySQL连接配置:掌握了mysql2驱动的使用和连接池的最佳配置
  2. SQL查询操作:学会了执行各种SQL语句和参数化查询防护
  3. 事务处理机制:理解了数据库事务的概念和复杂业务场景的实现
  4. ORM框架使用:掌握了Sequelize的模型定义、关联和高级查询
  5. 查询优化技巧:学会了SQL性能优化和索引使用策略
  6. 安全最佳实践:了解了SQL注入防护和数据库安全措施

🎯 MySQL操作下一步

  1. 高级SQL技巧:学习窗口函数、CTE、存储过程等高级特性
  2. 数据库设计:掌握数据库范式、索引设计和性能调优
  3. 读写分离:实现主从复制和读写分离架构
  4. 数据迁移:学习数据库版本管理和数据迁移工具

🔗 相关学习资源

💪 实践练习建议

  1. 构建电商系统:设计商品、订单、库存的复杂数据模型
  2. 实现内容管理系统:构建文章、分类、标签的关联关系
  3. 开发用户权限系统:实现RBAC权限控制模型
  4. 性能压测:使用大量数据测试查询性能和优化效果

🔍 常见问题FAQ

Q1: 什么时候选择原生SQL,什么时候使用ORM?

A: 选择依据:1)复杂查询和性能要求高时使用原生SQL;2)快速开发和维护性要求高时使用ORM;3)团队技能水平和项目复杂度;4)可以混合使用,在ORM中执行原生SQL。

Q2: 如何防止SQL注入攻击?

A: 防护措施:1)始终使用参数化查询;2)验证和清理用户输入;3)使用ORM框架的内置防护;4)限制数据库用户权限;5)定期安全审计;6)使用Web应用防火墙。

Q3: MySQL连接池应该如何配置?

A: 配置原则:1)根据并发量设置连接数;2)设置合理的超时时间;3)启用连接保活机制;4)监控连接池状态;5)考虑数据库服务器性能;6)测试不同配置的性能表现。

Q4: 如何优化MySQL查询性能?

A: 优化策略:1)创建合适的索引;2)避免SELECT *;3)使用LIMIT分页;4)优化WHERE条件;5)使用EXPLAIN分析查询;6)避免子查询,使用JOIN;7)合理使用缓存。

Q5: 数据库事务什么时候使用?

A: 使用场景:1)多表操作需要保证一致性;2)金融交易等关键业务;3)批量操作需要原子性;4)数据完整性要求高的场景;5)需要回滚机制的操作。


"MySQL作为成熟的关系型数据库,为企业级应用提供了可靠的数据存储解决方案。掌握了MySQL操作技术,你就具备了处理复杂业务数据的重要能力。继续学习数据模型设计,提升你的数据库架构技能!"