Grails сервис транзакционного поведения - PullRequest
6 голосов
/ 23 января 2012

В приложении Grails поведение сервисных методов по умолчанию заключается в том, что они являются транзакционными, и транзакция автоматически откатывается при возникновении непроверенного исключения. Однако в Groovy никто не вынужден обрабатывать (или перебрасывать) проверенные исключения, поэтому существует риск того, что если метод службы выдает проверенное исключение, транзакция не будет откатываться. В связи с этим представляется целесообразным аннотировать каждый класс обслуживания Grails

@Transactional(rollbackFor = Throwable.class) 
class MyService {

    void writeSomething() {
    }
}

Предположим, у меня есть другие методы в MyService, один из которых читает только БД, а другой не касается БД, правильны ли следующие аннотации?

@Transactional(readOnly = true)
void readSomething() {}

// Maybe this should be propagation = Propagation.NOT_SUPPORTED instead?
@Transactional(propagation = Propagation.SUPPORTS)
void dontReadOrWrite() {}

Чтобы ответить на этот вопрос, я думаю, вам нужно знать, что я собираюсь сделать:

  • Если исключение выдается из какого-либо метода и выполняется транзакция, он будет откатан. Например, если writeSomething() вызывает dontReadOrWrite(), и из последнего выдается исключение, транзакция, запущенная первым, будет откатываться. Я предполагаю, что атрибут уровня класса rollbackFor наследуется отдельными методами, если они явно не переопределяют его.
  • Если транзакция не выполняется, она не будет запущена для таких методов, как dontReadOrWrite
  • Если при вызове readSomething() транзакция не выполняется, будет запущена транзакция только для чтения. Если транзакция чтения-записи выполняется, она будет участвовать в этой транзакции.

Ответы [ 2 ]

4 голосов
/ 24 января 2013

Ваш код верен настолько, насколько это возможно: вы хотите использовать аннотацию Spring @Transactional для отдельных методов в своем классе обслуживания, чтобы получить гранулярность, которую вы ищете, вы правы, что вы хотите SUPPORTS для dontReadOrWrite (NOT_SUPPORTED приостановит существующую транзакцию, которая не купит вам ничего, основываясь на том, что вы описали, и потребует от вашего программного обеспечения тратить циклы, так что нет никакой выгоды), и вы правы, что хотите распространение по умолчанию поведение (ОБЯЗАТЕЛЬНО) для readSomething.

Но важно помнить о поведении транзакций в Spring: Spring реализует управление транзакциями, оборачивая ваш класс в прокси, который выполняет соответствующую настройку транзакции, вызывает ваш метод, а затем выполняет соответствующую транзакцию, когда управление прекращается. возвращается. И (что крайне важно), этот код управления транзакциями только вызывается при вызове метода на прокси-сервере, что не происходит, если writeSomething () напрямую вызывает dontReadOrWrite (), как в вашем первом пуле.

Если вам нужно другое поведение транзакций для метода, вызываемого другим методом, у вас есть два варианта, которые я знаю, если вы хотите продолжать использовать аннотации Spring @Transactional для управления транзакциями:

  1. Переместите вызываемый другим методом метод в другой класс обслуживания, к которому будет обращаться из исходного класса обслуживания через прокси Spring.
  2. Оставьте метод там, где он есть. Объявите переменную-член в вашем классе обслуживания того же типа, что и интерфейс вашего класса обслуживания, и сделайте ее @Autowired, которая даст вам ссылку на прокси-объект Spring вашего класса обслуживания. Затем, когда вы захотите вызвать свой метод с другим поведением транзакций, сделайте это для этой переменной-члена, а не напрямую, и код транзакции Spring будет запускаться так, как вы этого хотите.

Подход № 1 хорош, если эти два метода действительно не связаны между собой, потому что он решает вашу проблему, не сбивая с толку тех, кто заканчивает тем, что поддерживает ваш код, и нет никакого способа случайно забыть вызвать метод с поддержкой транзакций.

Подход №2, как правило, является лучшим вариантом, если предположить, что все ваши методы находятся в одном сервисе по какой-то причине и что вы не захотите их разделять. Но это сбивает с толку сопровождающего, который не понимает эту мелочь транзакций Spring, и вы должны помнить, чтобы вызывать его таким образом, в каждом месте, где вы его называете, так что есть цена. Я обычно готов заплатить эту цену, чтобы не раскалывать свои классы обслуживания неестественно, но, как всегда, это будет зависеть от вашей ситуации.

1 голос
/ 20 февраля 2012

Я думаю, что вам нужно более детальное управление транзакциями, и использование аннотации @Transactional является правильным направлением для этого.Тем не менее, есть Плагин обработки транзакций Grails , который может дать вам поведение, которое вы ищете.Предостережение заключается в том, что вам нужно будет обернуть вызовы вашего сервисного метода в замыкание DomainClass.withTransaction и предоставить нестандартное поведение, которое вы ищете в качестве карты параметров, для метода withTransaction ().

Какобратите внимание, что на бэкэнде выполняется именно то, о чем вы говорите выше, используя аннотацию @Transactional, чтобы изменить поведение транзакции во время выполнения.Документация к плагину отличная, поэтому я не думаю, что вы окажетесь без достаточного руководства.

Надеюсь, это то, что вы ищете.

...