Обновится ли сущность, если в транзакции обновить поля сущности и без вызова save

Ответ

В JPA/Hibernate сущность обновится в БД даже без явного вызова save(), если изменения произошли внутри активной транзакции и транзакция завершилась успешно (commit). Это работает благодаря механизму автоматического "dirty checking".

Пример:

@Transactional
public void updateUser(Long userId) {
    User user = userRepository.findById(userId).orElseThrow();
    user.setName("New Name"); // Изменение будет сохранено при коммите
    // save() не вызывается явно
}

Условия для автоматического обновления:

  1. Сущность находится в состоянии managed (привязана к текущей сессии)
  2. Изменения происходят внутри транзакции
  3. Транзакция завершается успешно (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()! Вообще нихуя!
}

И всё, имя в базе поменяется. Пиздец магия, да? Но не всё так просто, как кажется. Чтобы эта магия сработала, нужно соблюсти три условия, а то будет облом полный:

  1. Сущность должна быть в состоянии "managed". То есть ты её только что из базы вытащил через тот же Hibernate, или save()-нул, или merge()-нул. Если ты её сам, с бодуна, через new User() создал — это просто кусок памяти, Java-объект, и Hibernate про него нихуя не знает. Ему похуй.
  2. Изменения должны быть внутри транзакции. Всё, что ты делаешь между @Transactional или между ручным transaction.begin() и commit() — под прицелом. Сделал что-то до или после — иди нахуй, никто не заметит.
  3. Транзакция должна успешно закоммититься. Если в конце вылетит исключение и будет rollback, то все твои изменения, даже самые гениальные, накрываются медным тазом. Как будто их и не было.

А теперь главный подвох, на котором все обжигаются, ёпта! Если ты поставишь @Transactional(readOnly = true), то можешь хоть обоссаться, меняя поля — в базу нихуя не уйдёт. Потому что readOnly — это намёк фреймворку, что тут только читают, и он оптимизации всякие включает, а dirty checking может отключить нахуй.

Вот так вот, блядь. Удобно, но надо головой думать, а то потом волнение ебать — почему данные не обновились, а ты, оказывается, вне транзакции или readOnly поставил.