Ответ
В JPA/Hibernate сущность обновится в БД даже без явного вызова save(), если изменения произошли внутри активной транзакции и транзакция завершилась успешно (commit). Это работает благодаря механизму автоматического "dirty checking".
Пример:
@Transactional
public void updateUser(Long userId) {
User user = userRepository.findById(userId).orElseThrow();
user.setName("New Name"); // Изменение будет сохранено при коммите
// save() не вызывается явно
}
Условия для автоматического обновления:
- Сущность находится в состоянии managed (привязана к текущей сессии)
- Изменения происходят внутри транзакции
- Транзакция завершается успешно (commit)
Исключение: если используется readOnly=true в транзакции, изменения не сохранятся.
Ответ 18+ 🔞
О, слушай, вот это прикол-то! Ты можешь в своей сущности поменять что угодно, и в базу это улетит, даже если ты не кричишь на неё save() или merge(). Как будто бы ты и не просил, а оно само, понимаешь?
Это, блядь, магия Hibernate, называется "dirty checking". Он, сука, как хитрая жопа, следит за всеми своими managed-объектами. Как только транзакция закрывается и коммитится, он быстренько так пробегается по всем, кого помнит, и спрашивает: "Ну что, мудила, менялся?" И если менялся — хоп, и UPDATE в базу.
Вот смотри, как это просто выглядит:
@Transactional
public void updateUser(Long userId) {
User user = userRepository.findById(userId).orElseThrow();
user.setName("New Name"); // Меняем имя прямо на ходу
// И НИКАКОГО save()! Вообще нихуя!
}
И всё, имя в базе поменяется. Пиздец магия, да? Но не всё так просто, как кажется. Чтобы эта магия сработала, нужно соблюсти три условия, а то будет облом полный:
- Сущность должна быть в состоянии "managed". То есть ты её только что из базы вытащил через тот же Hibernate, или
save()-нул, илиmerge()-нул. Если ты её сам, с бодуна, черезnew User()создал — это просто кусок памяти, Java-объект, и Hibernate про него нихуя не знает. Ему похуй. - Изменения должны быть внутри транзакции. Всё, что ты делаешь между
@Transactionalили между ручнымtransaction.begin()иcommit()— под прицелом. Сделал что-то до или после — иди нахуй, никто не заметит. - Транзакция должна успешно закоммититься. Если в конце вылетит исключение и будет rollback, то все твои изменения, даже самые гениальные, накрываются медным тазом. Как будто их и не было.
А теперь главный подвох, на котором все обжигаются, ёпта! Если ты поставишь @Transactional(readOnly = true), то можешь хоть обоссаться, меняя поля — в базу нихуя не уйдёт. Потому что readOnly — это намёк фреймворку, что тут только читают, и он оптимизации всякие включает, а dirty checking может отключить нахуй.
Вот так вот, блядь. Удобно, но надо головой думать, а то потом волнение ебать — почему данные не обновились, а ты, оказывается, вне транзакции или readOnly поставил.