Sequelize:使用 manyToMany 关联创建时避免重复

Sequelize: Avoid duplicates when create with manyToMany associations

我正在开发一个小型电子商务来销售电影票。我正在使用 Sequelize 来设计所有模型及其关联。我面临的问题与为创建最终订单而定义的路线有关:

Order.create({
        userId: req.body.userId,                   // Number
        sessionId: req.body.sessionId,             // Number
        seats: req.body.seats,                     // Array of objects
        offsiteProducts: req.body.offsiteProducts  // Array of objects
    },
    {
    include: [
        {
            model: Seat,
            attributes: ['area', 'number', 'roomId']
        },
        {
            model: OffsiteProduct,
            attributes: ['name', 'unitPrice']
        }
    ]
}).then(order => {
    res.json(order);
})

模型之间的关系如下:

User.hasMany(Order);
Order.belongsTo(User);

Session.hasMany(Order);
Order.belongsTo(Session);

Seat.belongsToMany(Order, { through: "reserved_seats" });
Order.belongsToMany(Seat, { through: "reserved_seats" });

OffsiteProduct.belongsToMany(Order, { through: ReservedOffsiteProduct });
Order.belongsToMany(OffsiteProduct, { through: ReservedOffsiteProduct });

对于“一对多”关系,传递外键足以让 Sequelize 正确关联模型。

但对于“多对多”关联(Sequelize 中的“belongsToMany”)它会重复 为席位输入的数据和场外产品,并将其创建为订单的一部分,并分别作为新的独立席位和场外产品创建。

如何避免这种行为并仅在最终订单中包含座位和场外产品数组?谢谢

执行此操作的一种方法是在事务中执行所有内容。这样,它将是原子的,即“全有或全无”。

Sequelizemany-to-many associations提供了一些方法。在这种情况下,可以为每个联结点单独调用 table。

考虑到这一点,以下内容应该可以完成没有任何重复的插入:

let t = await sequelize.transaction()

try { 
    let order = await Order.create({
                userId: req.body.userId,
                sessionId: req.body.sessionId
            }, {
                transaction: t
            })

    let seats = await Seat.findAll({
            where: {
                id: {
                    [Op.in]: req.body.seatIds
                }
            }
        })
    let offsiteProducts = await OffsiteProducts.findAll({
            where: {
                id: {
                    [Op.in]: req.body.offsiteProductIds
                }
            }
        })
    
    await order.addSeats(seats, { transaction: t })
    await order.addOffsiteProducts(offsiteProducts, { transaction: t })

    await t.commit()
} catch (err) {
    if (t) {
        await t.rollback()
    }
}

或者,如果 OffsiteProductsSeats table 中的行的主键是已知的,则上面的内容可以缩短为:

let t = await sequelize.transaction()

try { 
    let order = await Order.create({
                userId: req.body.userId,
                sessionId: req.body.sessionId
            }, {
                transaction: t
            })

    await order.addSeats(req.body.seatIds, { transaction: t })
    await order.addOffsiteProducts(req.body.offsiteProductIds, { transaction: t })

    await t.commit()
} catch (err) {
    if (t) {
        await t.rollback()
    }
}

在文档 here.

中有更多关于将主键数组传递给 .addSeats.addOffsiteProducts 方法的解释