SequelizeJS中复杂多对多(多对多对多)关联的FindAll查询问题
嘿,关于Sequelize里这种带额外字段的多对多关联查询,我刚好有不少经验,给你一步步拆解一下!核心思路是把中间表显式定义成独立模型,而不是让Sequelize自动生成,这样就能轻松访问中间表的额外字段(比如这里的price)了。
第一步:定义所有模型
首先得把三个模型都定义好,包括中间表CompanyProduct:
Company模型
const { DataTypes } = require('sequelize'); const Company = sequelize.define('Company', { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, name: { type: DataTypes.STRING, allowNull: false, comment: '公司名称' } });
Product模型
const Product = sequelize.define('Product', { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, name: { type: DataTypes.STRING, allowNull: false, comment: '产品名称' } });
中间表CompanyProduct模型
这个是关键,要包含price字段,以及关联两边的外键:
const CompanyProduct = sequelize.define('CompanyProduct', { price: { type: DataTypes.DECIMAL(10, 2), allowNull: false, comment: '该公司对应产品的售价' }, // 外键可以显式定义,也可以让关联自动生成,显式定义更清晰 companyId: { type: DataTypes.INTEGER, references: { model: Company, key: 'id' } }, productId: { type: DataTypes.INTEGER, references: { model: Product, key: 'id' } } });
第二步:设置关联关系
接下来要建立模型之间的关联,因为中间表有额外字段,要用belongsToMany时指定through为我们定义的中间表模型,同时明确外键和别名:
// 公司 ↔ 产品 的多对多关联 Company.belongsToMany(Product, { through: CompanyProduct, // 指定中间表模型 foreignKey: 'companyId', // 中间表中指向Company的外键 otherKey: 'productId', // 中间表中指向Product的外键 as: 'products' // 别名,查询时用来引用关联的产品 }); Product.belongsToMany(Company, { through: CompanyProduct, foreignKey: 'productId', otherKey: 'companyId', as: 'companies' // 别名,查询时用来引用关联的公司 }); // 额外:给中间表和两边模型建立一对多关联(可选,但某些场景下查询更灵活) Company.hasMany(CompanyProduct, { foreignKey: 'companyId', as: 'productPricings' }); CompanyProduct.belongsTo(Company, { foreignKey: 'companyId' }); Product.hasMany(CompanyProduct, { foreignKey: 'productId', as: 'companyPricings' }); CompanyProduct.belongsTo(Product, { foreignKey: 'productId' });
第三步:执行FindAll查询
现在就可以写各种查询了,下面是几个常用场景:
场景1:查询所有公司,附带它们的产品和对应售价
const allCompaniesWithProducts = await Company.findAll({ include: [ { model: Product, as: 'products', // 对应关联时设置的别名 through: { attributes: ['price'], // 指定要返回的中间表字段 as: 'pricing' // 给中间表数据起个友好的别名,方便后续取值 } } ] }); // 返回结果示例: // allCompaniesWithProducts[0] = { // id: 1, // name: 'XX公司', // products: [ // { // id: 1, // name: '汽车', // pricing: { price: 250000.00 } // }, // { // id: 2, // name: '自行车', // pricing: { price: 1500.00 } // } // ] // }
场景2:查询所有产品,附带生产它们的公司和对应售价
const allProductsWithCompanies = await Product.findAll({ include: [ { model: Company, as: 'companies', through: { attributes: ['price'], as: 'pricing' } } ] });
场景3:带过滤条件的查询(比如只查售价大于1000的产品及其公司)
const { Op } = require('sequelize'); const filteredProducts = await Product.findAll({ include: [ { model: Company, as: 'companies', through: { attributes: ['price'], as: 'pricing', where: { price: { [Op.gt]: 1000 } } // 过滤售价大于1000的记录 }, required: true // 加上这个相当于INNER JOIN,只返回有符合条件公司的产品 } ], order: [['name', 'ASC']], // 按产品名称升序排序 limit: 10, // 分页:每页10条 offset: 0 // 从第0条开始(第一页) });
场景4:直接查询中间表的完整关联数据
如果需要更灵活的组合,也可以直接查询中间表,同时关联两边的模型:
const companyProductDetails = await CompanyProduct.findAll({ include: [ { model: Company, as: 'Company' }, { model: Product, as: 'Product' } ], where: { price: { [Op.between]: [500, 500000] } }, // 过滤价格区间 order: [['price', 'DESC']] // 按售价降序排序 });
内容的提问来源于stack exchange,提问作者Pedro A
相关产品推荐
相关产品推荐

