Grails GORM gotchas and tricks: используйте read() вместо get() в update actions


Попался на интересную проблему в любимом Grails. Как и 90% всех проблем это от незнания тонкостей нижележащего Hibernate.
Есть update action на который мы сабмитим форму редактирования объекта, например Person. Делаем валидацию которая если не прошла, возвращаемся опять в режим редактирования.
Если ошибок нет, сохраняем объект. Ну вообщем классика жанра:

def update(Long id) {
  def person = Person.get(id)
  person.properties = params
  person.validate()
  if (person.hasErrors()) {
    render(view: 'edit', model: [person: person])
    return
  }
  person.save(flush: true, failOnError: true)
}

И тут оказывается что если валидация не прошла, то сами изменения почему-то всё равно сохранились в объекте! А ведь мы точно не дёргали save().
Всё дело в dirty-checking, если вы поменяли поле в объекте то он таки уже сохранится в БД таким. К тому же на каждый action контроллера всегда открывается и флашится транзакция.
Избежать можно двумя способами: склонировать объект отдельно или заменить get() на read(). Т.е. было:

def person = Person.get(id)

стало:

def person = Person.read(id)

У объекта который вернулся через read() отключено dirty checking, и для сохранения объекта требуется явно вызвать save().
Сразу же возникает вопрос, почему же тогда везде в документации используется get()? Если вкратце то объект полученный через get() может частично обновляться только по изменённым полям, в случае с read() будет перегенериватся SQL со всеми полями.
Надеюсь кому то спас несколько часов дебага 🙂

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s