使用Sequelize关联查询员工与休假数据异常问题求助
问题描述
通过nomorInduk字段关联查询Karyawan(员工)和Cuti(休假)数据,已配置模型关联,但查询结果不符合预期。
相关代码
Karyawan 模型文件
'use strict'; module.exports = (sequelize, DataTypes) => { const Karyawan = sequelize.define('Karyawan', { nomorInduk: DataTypes.STRING, nama: DataTypes.STRING, alamat: DataTypes.STRING, tanggalLahir: DataTypes.DATE, tanggalBergabung: DataTypes.DATE }, {}); Karyawan.associate = function (models) { Karyawan.hasMany(models.Cuti, { foreignKey: 'nomorInduk' }); }; return Karyawan; };
模型入口文件
'use strict'; const Sequelize = require('sequelize'); const dotenv = require('dotenv'); const db = {}; const KaryawanModel = require('./karyawan'); const CutiModel = require('./cuti'); dotenv.config(); const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, { host: process.env.DB_HOST, dialect: 'mysql', logging: console.log }); sequelize.sync({ force: false }) .then(() => { console.log('Database synchronized'); }) .catch((error) => { console.error('Failed to synchronize database:', error); }); const Karyawan = KaryawanModel(sequelize, Sequelize); const Cuti = CutiModel(sequelize, Sequelize); db.sequelize = sequelize; db.Sequelize = Sequelize; // Set up associations inside the models Karyawan.associate({ Cuti }); Cuti.associate({ Karyawan }); module.exports = { db, Karyawan, Cuti };
Cuti 模型文件
'use strict'; module.exports = (sequelize, DataTypes) => { const Cuti = sequelize.define('Cuti', { nomorInduk: DataTypes.STRING, tanggalCuti: DataTypes.DATE, lamaCuti: DataTypes.INTEGER, keterangan: DataTypes.TEXT }, {}); Cuti.associate = function (models) { Cuti.belongsTo(models.Karyawan, { foreignKey: 'nomorInduk' }); }; return Cuti; };
查询处理函数
async function getKaryawanByNomorInduk(req, res) { try { const nomorInduk = req.params.nomorInduk; const karyawan = await models.Karyawan.findOne({ where: { nomorInduk: nomorInduk }, include: [{ model: models.Cuti, attributes: ['id', 'nomorInduk', 'tanggalCuti', 'lamaCuti', 'keterangan'], }], }); if (!karyawan) { return res.status(404).json({ message: 'Tidak ada data karyawan dengan nomor induk tersebut.' }); } const result = { status: 'ok', data: karyawan, }; res.json(result); } catch (error) { res.status(500).json({ message: 'Gagal mendapatkan data karyawan.', error: error.message }); } }
结果差异
- 当前返回:单个员工对象,其中包含一个
Cuti数组,数组内是该员工的所有休假记录 - 期望返回:多条数据,每条数据同时包含员工信息和单条休假信息(类似SQL JOIN后的扁平化结果)
解决方案
问题出在使用了findOne和默认的关联查询格式,调整查询方式即可得到扁平化的JOIN结果:
修改后的查询处理函数
async function getKaryawanByNomorInduk(req, res) { try { const nomorInduk = req.params.nomorInduk; // 使用findAll + raw: true 获取扁平化的JOIN结果 const karyawanCutiList = await models.Karyawan.findAll({ where: { nomorInduk: nomorInduk }, include: [{ model: models.Cuti, attributes: ['id', 'tanggalCuti', 'lamaCuti', 'keterangan'], required: false // 允许员工无休假记录时也返回数据 }], raw: true, // 直接返回数据库原始查询数据,不包装为模型实例 nest: false // 将关联字段扁平化到顶层,而非嵌套结构 }); if (karyawanCutiList.length === 0) { return res.status(404).json({ message: 'Tidak ada data karyawan dengan nomor induk tersebut.' }); } const result = { status: 'ok', data: karyawanCutiList, }; res.json(result); } catch (error) { res.status(500).json({ message: 'Gagal mendapatkan data karyawan.', error: error.message }); } }
关键调整说明
- 替换
findOne为findAll:适配一个员工对应多条休假记录的场景,返回多条结果 - 添加
raw: true:跳过Sequelize的模型实例包装,直接返回数据库查询的原始数据 - 设置
nest: false:将关联的Cuti字段扁平化到顶层,避免嵌套结构 - 调整Cuti的
attributes:移除重复的nomorInduk字段,避免结果中出现冗余数据 - 可选
required: false:若需要在员工无休假记录时仍返回员工信息则保留;若仅需返回有休假记录的员工,可删除该配置
内容的提问来源于stack exchange,提问作者NinjaTea0161
相关产品推荐
相关产品推荐

