#javascript #orm #sequelize.js
#javascript #orm #sequelize.js
Вопрос:
У меня возникла концептуальная проблема с тем, как обновить все экземпляры модели после ее обновления.
Представьте себе следующий метод renameUser
(может быть любой ORM):
async function renameUser(userId: number, newUsername: string) {
const user = await User.findByPk(userId);
await user.update({ username: newUsername });
}
И следующее использование:
const user = await User.create({ username: "old" });
// user.username === "old"
this.renameUser(user.id, "new");
// still, user.username === "old"
Очевидно, что этой проблемы не было бы, если бы я передавал user
объект непосредственно в метод update, но это работает только в этом простом примере — в моем случае фактически невозможно использовать один и тот же экземпляр, поскольку он может быть изменен с помощью перехватов в совершенно другом контексте.
Итак, одним из простых решений было бы вызвать user.reload()
после вызова, который будет извлекать последние пользовательские данные из базы данных:
const user = await User.create({ username: "old" });
// user.username === "old"
this.renameUser(user.id, "new");
user.reload();
// now: user.username === "new"
Однако для этого мне нужно знать, что renameUser
метод изменит мой пользовательский объект. В этом случае это очевидно, но если вызывается метод, это не всегда возможно.
Есть ли какой-либо шаблон, который я могу использовать для решения этой проблемы?Мне пришло в голову создать a UserFactory
, который гарантирует, что у меня есть только один экземпляр пользователя (индексируемый его первичным ключом) в любое время, а затем обновить этот экземпляр. Но мне было интересно, как другие решают это? Это распространенная проблема?
Ответ №1:
Почему вы не используете .save()
updateUser
функцию in?
const renameUser = async (userId, newUsername) => {
const user = await User.findByPk(userId);
user.username = newUsername;
await user.save();
return user;
}
и используйте это так
const user = await User.create({ username: "old" });
// user.username === "old"
user = await this.renameUser(user.id, "new");
// now, user.username === "new"
Комментарии:
1. См. Мой вопрос: «Очевидно, что этой проблемы не было бы, если бы я передавал объект user непосредственно в метод update, но это работает только в этом простом примере — в моем случае фактически невозможно использовать один и тот же экземпляр, поскольку он может быть изменен с помощью перехватов в совершенно другом контексте.»
Ответ №2:
Вы можете выполнять свои запросы более эффективно, просто запустив обновление с помощью Model.update()
функции вместо запроса экземпляра и последующего его обновления.
async function renameUser(userId: number, newUsername: string) {
const [ updatedRowCount, updateRowOnPostgresOnly ] = await User.update({
username: newUsername,
}, {
where: {
id: userId,
},
});
// you can use the updated row count to see if the user changed.
const isUpdated = updatedRowCount > 0;
return isUpdated;
}
Теперь вы можете дождаться результата, чтобы увидеть, изменилась ли строка, даже не загружая ее.
const user = await User.create({ username: "old" });
// user.username === "old"
const isUpdated = await this.renameUser(user.id, "new");
if (isUpdated) {
user.reload();
// now: user.username === "new"
}
Обратите внимание, что в вашем примере вы не используете
await
async runameUser()
функцию on.
Это немного отличается от вашего вопроса, но если вы уже работаете с экземплярами модели, то вы можете использовать Instance.changed()
для получения измененных полей.
const user = await User.create({ username: "old" });
user.username = "old";
const changed = user.changed(); // == [ "username" ]
if (changed.length) {
await user.save();
}
* Обратите внимание, что в этом примере он проверяет
changed.length
, но на самом деле Sequelize ничего не сделает, если выawait instance.save()
и ничегоinstance.changed()
— измените осведомленность о сохранении.