понедельник, 24 декабря 2012 г.

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 со всеми полями.


Надеюсь кому то спас несколько часов дебага :)

Комментариев нет:

Отправить комментарий