Sequelize Unknown Column 错误处理:不报错返回 NULL
2025-03-10 00:22:42
Sequelize 中 "Unknown column" 错误的处理方法:避免报错并返回 NULL
遇到 Sequelize 报 "Unknown column" 错误,通常是因为数据库表里少了某个字段。开发环境报这个错挺正常的,但在生产环境,我们更希望程序能稳点,别直接崩溃,最好是找不到字段就返回个 NULL。
问题原因:字段缺失
问题很直接:Sequelize 查询时用到的某个字段,在数据库表里找不到了。这可能是有人不小心删了字段,或者数据库迁移出了岔子。
拿咱们碰到的这个情况来说:
SELECT `ID`, `free_delivery_sum`, `other_field` FROM `s4y_fillials`;
free_delivery_sum
字段没了,Sequelize 找不到,就报 "Unknown column 'free_delivery_sum' in 'field list'"。
我们想要的结果是,即使 free_delivery_sum
没了,也别报错,返回个像这样的东西:{ID:1,free_delivery_sum:null,other_field:"some_data"}
。
解决方案:不改数据库,搞定问题
接下来,咱们不用去数据库里把字段加回去,也能解决这个问题。下面有几个办法,根据实际情况选用:
1. 使用 attributes
选项 (推荐)
这是个好办法,直接告诉 Sequelize 你要查哪些字段。就算数据库里有多的字段,或者少了某个字段,Sequelize 都不会管,只按你说的来。
原理:
Sequelize 的 findAll
方法(以及其他查询方法)有个 attributes
选项,可以让你精确控制要查哪些字段。
代码示例:
const FilialDBModel = require('./models/filial'); // 假设这是你的模型
async function getFillials() {
try {
const fillials = await FilialDBModel.findAll({
attributes: ['ID', 'other_field'] // 只查这两个字段
});
console.log(fillials);
//如果 free_delivery_sum 不存在也正常运行,不会报错。
// fillials 里的每个对象都只有 ID 和 other_field 这两个属性
} catch (error) {
console.error("出错了:", error);
}
}
getFillials();
进阶使用技巧:
-
包含和排除:
attributes
不光能指定要哪些字段,还能指定不要哪些字段。attributes: { exclude: ['free_delivery_sum'] } // 排除 'free_delivery_sum'
或者
javascript const { QueryTypes } = require('sequelize'); //... const fillials = await sequelize.query("SELECT ID,other_field FROM s4y_fillials", { type: QueryTypes.SELECT }); //...
-
别名: 还可以给字段起个别名。
attributes: ['ID', ['other_field', 'renamed_field']] //把 other_field 重命名为 renamed_field
-
与
raw: true
结合使用如果使用
raw: true
,可以获得更纯粹的数据,减少ORM带来的额外开销.const fillials = await FilialDBModel.findAll({ attributes: ['ID', 'other_field'], raw: true });
安全建议:
明确指定要查询的字段,能减少暴露敏感数据的风险。
2. 使用 try...catch
捕获错误
这个方法比较直接,就是在代码里把可能出错的部分包起来,出错的时候做点特殊处理。
原理:
用 try...catch
包住 Sequelize 查询,如果查询出错(比如碰到 "Unknown column"),就在 catch
里处理错误,返回一个你想要的结果。
代码示例:
const FilialDBModel = require('./models/filial');
async function getFillials() {
try {
const fillials = await FilialDBModel.findAll();
console.log(fillials);
} catch (error) {
if (error.name === 'SequelizeDatabaseError' && error.message.includes('Unknown column')) {
// 如果是 "Unknown column" 错误
const defaultValues = { ID: null, free_delivery_sum: null, other_field: null }; //可以定义默认
const manuallyFetchedData = await FilialDBModel.findAll({
attributes: ['ID','other_field'], //用其他方法来替代,这样可以保留代码原本的结构。
raw:true,
});
const result = manuallyFetchedData.map(item=>({...defaultValues,...item}));
console.log("遇到'Unknown column'错误,返回:",result);
return result;
} else {
console.error("其他错误:", error);
}
}
}
getFillials();
进阶使用
可以进一步的将错误捕获,做成一个通用的工具函数,进行集中处理:
async function safeSequelizeQuery(model, queryOptions, defaultValues) {
try {
return await model.findAll(queryOptions);
} catch (error) {
if (error.name === 'SequelizeDatabaseError' && error.message.includes('Unknown column')) {
if(queryOptions.attributes && queryOptions.attributes.exclude)
{
//根据配置获取存在的
const realAttributes = queryOptions.attributes.exclude.filter(attribute=> !error.message.includes(attribute) );
const manuallyFetchedData = await model.findAll({
attributes: {exclude:realAttributes},
raw:true,
});
const result = manuallyFetchedData.map(item=>({...defaultValues,...item}));
return result;
}else{
const manuallyFetchedData = await model.findAll({
attributes: Object.keys(defaultValues),
raw:true,
});
const result = manuallyFetchedData.map(item=>({...defaultValues,...item}));
return result;
}
}
throw error; // 其他错误, 抛出去
}
}
// 用法示例
//假设defaultValues包含了所有数据库model的可能出现的字段。
const defaultValues = { ID: null, free_delivery_sum: null, other_field: null };
async function getFillials(){
const result = await safeSequelizeQuery(FilialDBModel, {}, defaultValues); //所有列
console.log(result);
}
async function getFillials2(){
const result = await safeSequelizeQuery(FilialDBModel, {attributes: { exclude: ['free_delivery_sum'] }}, defaultValues); //排除 free_delivery_sum
console.log(result);
}
安全建议:
错误处理要细致,不同的错误类型可能有不同的处理方式。另外,对于未知异常,最好往上抛出。
3. 使用虚拟字段(Computed Fields/ Virtual Fields)
Sequelize 允许定义虚拟字段。这些字段不在数据库里,而是在你获取数据的时候,通过一些计算得出来的。
原理:
在模型定义中使用 DataTypes.VIRTUAL
来定义一个虚拟字段。定义时可以指定一个 get
方法,该方法会在查询后,被调用。我们可以在 get
方法里对获取结果做一些操作。
代码:
// 在你的 FilialDBModel 模型定义里
const FilialDBModel = sequelize.define('s4y_fillials', {
ID: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
other_field: {
type: DataTypes.STRING,
allowNull: true
},
free_delivery_sum: {
type: DataTypes.VIRTUAL,
get() {
return null; // 直接返回 null
},
set(value) {
throw new Error('不要尝试设置虚拟字段!');
}
}
},{
timestamps:false //根据需添加
});
进阶用法
- 可以在虚拟字段里添加逻辑,让他能动态适应不同情况
free_delivery_sum: { type: DataTypes.VIRTUAL, get() { if (this.getDataValue('some_other_field') === 'some_value') { return this.getDataValue('another_field'); //根据不同条件做处理。 } return null; } }
安全建议:
虚拟字段不会存储在数据库,不会有数据泄露问题。
这几种方法,各有各的好处,在不改动数据库的情况下,都能解决 “Unknown Column” 错误,让你在生产环境下不报错,而是返回 NULL。 方法一因为更符合数据库操作习惯,更推荐。