Конечно, подход save (failOnError: true) в настоящее время дает слишком мало информации о том, какие проверки вызвали проблему. (Вы действительно хотите анализировать сообщения об исключениях в контроллере в любом случае?) Однако существует несколько альтернатив, которые вы могли бы рассмотреть для решения двух разных проблем, отката транзакции и передачи информации об ошибке проверки.
Одной из альтернатив является полное отсутствие сохранения данных. Во-первых, вызовите Child.validate (), чтобы увидеть, можно ли сохранить ребенка. Если ни одна валидация не удалась, validate () возвращает true, поэтому вызовите theChild.save (). При возникновении ошибок валидации они будут записаны в объекте ошибок в Child. Вновь созданный дочерний элемент, возвращенный вызывающему контроллеру, затем может быть проверен на наличие ошибок или просто отображен пользователю в представлении.
Другая альтернатива - не использовать декларативные транзакции; т.е. установить статическую транзакцию = false на сервисе. Тогда ваш код может управлять самой транзакцией; как то так:
Child.withTransaction { txStatus ->
try {
child.save(failOnError:true)
} catch (ValidationException e) {
// rollback, but swallow exception, requiring caller to check child for errors
txStatus.setRollbackOnly()
}
}
Наконец, ваш вопрос подразумевает, что вы хотите, чтобы контроллер что-то делал с информацией об ошибках, например, отображал исходную страницу, чтобы пользователь мог исправить записи. Так как проверяется ребенок, возвращение потомка не будет работать, когда выдается исключение (ваше или ValidationException). Однако у дочернего элемента будут ошибки, заполняемые во время вызова save (). Вместо того, чтобы передавать String и Parent, а затем создавать экземпляр Child в сервисе, вы должны рассмотреть создание экземпляра Child в вызывающей стороне и передать его по ссылке. Затем, когда save () завершается ошибкой, дочерние ошибки будут заполняться, даже если save (failOnError: true) приводит к ValidationException. Вызывающая сторона может перехватить исключение ValidationException и затем передать дочерний элемент как часть модели для представления.
Лично я думаю, что первая альтернатива (сначала вызовите validate ()) или последняя (создание дочернего объекта вне службы) предпочтительнее, чем управление транзакциями вручную. В Grails декларативное управление транзакциями - это все или ничего для всего сервиса. То есть, если вы установите статические транзакции = false для службы, вам придется вручную управлять всеми транзакциями в каждом методе службы. Принцип KISS должен применяться здесь.