如何用Umzug执行Sequelize-CLI迁移文件?迁移异常排查
问题描述
我注释掉了sequelize.sync以避免自动生成表,目的是验证dbmigrate.js的有效性。但现在调用该文件时仅创建sequelizemeta表,执行npx sequelize db:migrate命令却能正常生成其他业务表,全程无任何报错,无法定位问题所在。
初始化数据库并调用迁移、种子文件的文件(data/index.js)
// data/index.js const config = require('config'); const { getLogger, } = require('../core/logging'); const { sequelize, } = require('./models'); const connectionTest = require('./tools/dbtest'); const migrate = require('./tools/dbmigrate'); //const seed = require('./tools/dbseed'); const isDevelopment = () => { return config.get('env') === 'development' ? true : false; }; async function initializeData() { // Syncs the database and uses alter to push the model changes if the table already exists /* await sequelize.sync({ alter: true, }); */ // Test the Connection await connectionTest(); // Migrate await migrate(); // Seed //if (isDevelopment()) await seed(); } async function shutdownData() { const logger = getLogger(); logger.info('Shutting down database connection'); await sequelize.close(); logger.info('Database connection closed'); } module.exports = { initializeData, shutdownData, };
Sequelize实例创建代码(data/models/index.js)
// data/models/index.js 'use strict'; // Generated using npx sequelize init const fs = require('fs'); const path = require('path'); const Sequelize = require('sequelize'); const basename = path.basename(__filename); // Added by me const config = require('config'); // Deconstructing database variables const [DATABASE_NAME, DATABASE_USERNAME, DATABASE_PASSWORD, DATABASE_HOST, DATABASE_DIALECT, DATABASE_PORT] = [config.get('database.name'), config.get('database.username'), config.get('database.password'), config.get('database.host'), config.get('database.dialect'), config.get('database.port')]; const db = {}; // Initializing the Sequelize database // npx sequelize init puts there database in the models folder, // for that reason I keep it here (data/models/index.js) instead of moving it to the index.js in the data folder const sequelize = new Sequelize( DATABASE_NAME, DATABASE_USERNAME, DATABASE_PASSWORD, { host: DATABASE_HOST, dialect: DATABASE_DIALECT, port: DATABASE_PORT, logging: true, }, ); fs .readdirSync(__dirname) .filter((file) => { return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); }) .forEach((file) => { const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes); db[model.name] = model; }); Object.keys(db).forEach((modelName) => { if (db[modelName].associate) { db[modelName].associate(db); } }); db.sequelize = sequelize; module.exports = db;
迁移代码(问题所在,data/tools/dbmigrate.js)
// data/tools/dbmigrate.js const { Umzug, SequelizeStorage, } = require('umzug'); const { getLogger, } = require('../../core/logging'); const { sequelize, } = require('../models/'); module.exports = async function migrate() { const logger = getLogger(); const migrator = new Umzug({ migrations: { glob: '{../migrations/*.js}', }, context: sequelize.getQueryInterface(), storage: new SequelizeStorage({ sequelize: sequelize, }), logger: console, }); let migrationSuccessful = true; try { await migrator.up(); logger.info('Succesful migration'); } catch (error) { migrationSuccessful = false; logger.error('Error while migrating the database', { error, }); } if (!migrationSuccessful) { try { await migrator.down(); logger.info('Succesful migration after reverting the latest migration'); } catch (error) { logger.error('Error while migrating the database', { error, }); } throw new Error('Migrations failed'); } };
迁移文件示例(由Sequelize-CLI生成,data/migrations/20221121195900-create-user.js)
// data/migrations/20221121195900-create-user.js 'use strict'; /** @type {import('sequelize-cli').Migration} */ module.exports = { async up(queryInterface, DataTypes) { await queryInterface.createTable('users', { userID: { primaryKey: true, type: DataTypes.UUID, allowNull: false, }, firstName: { type: DataTypes.STRING, allowNull: false, }, lastName: { type: DataTypes.STRING, allowNull: false, }, email: { type: DataTypes.STRING, allowNull: false, }, password: { type: DataTypes.STRING, allowNull: false, }, dateOfBirth: { type: DataTypes.DATE, allowNull: false, }, gender: { type: DataTypes.ENUM('M', 'F'), allowNull: false, }, createdAt: { allowNull: false, type: DataTypes.DATE, }, updatedAt: { allowNull: false, type: DataTypes.DATE, }, }); }, async down(queryInterface) { await queryInterface.dropTable('users'); }, };
问题分析与解决
核心问题出在dbmigrate.js的迁移文件路径配置上,Umzug没有正确找到你的迁移文件,所以只创建了记录迁移状态的sequelizemeta表,却没执行业务表的迁移脚本。
修复方案
1. 修正glob路径格式
当前的glob: '{../migrations/*.js}'写法存在冗余,Umzug的glob配置不需要外层大括号,直接修改为:
const migrator = new Umzug({ migrations: { glob: '../migrations/*.js', // 移除多余的大括号 }, // 其他配置保持不变 });
2. 改用绝对路径(更可靠)
相对路径容易受执行环境影响,推荐用path模块生成绝对路径,彻底避免路径解析错误:
const path = require('path'); // 顶部引入path模块 // ... 其他代码 const migrator = new Umzug({ migrations: { glob: path.join(__dirname, '../migrations/*.js'), // 生成绝对路径 }, // 其他配置保持不变 });
3. 验证迁移文件检测结果
可以在migrate函数中添加调试代码,确认Umzug是否正确检测到迁移文件:
// 在await migrator.up()之前添加 const migrations = await migrator.list(); console.log('检测到的迁移文件:', migrations);
修改后重新调用initializeData,Umzug就能正确找到并执行迁移脚本,生成对应的业务表。
内容的提问来源于stack exchange,提问作者M4themat1c5
相关产品推荐
相关产品推荐

