Как вызвать пользовательский метод отката в Spring Transaction Management? - PullRequest
10 голосов
/ 16 апреля 2011

Среда: Spring 3, Управление пользовательскими транзакциями, транзакции JDBC

Я только что прочитал документы Spring об использовании шаблона транзакции для управления транзакциями. Это казалось слишком сложным , поэтому я хочу спросить:

Большинство моих транзакций связаны с JDBC, то есть я просто объявляю @Transactional на своем сервисе.Но теперь Я выполняю вызов службы REST для другого сайта, который необходимо откатить, если какая-либо из следующих операций JDBC завершится неудачей , в этом случае я предоставлю код отката.

Как япрогресс в моем методе, в моей транзакции - я хочу сохранить ссылку на вызов службы REST (необходим для отката этого действия), и в случае исключения я просто хочу метод myCustomRollback(), вызываемый, который может получить доступ к ранее сохраненному объекту.

Почему бы просто не предоставить карту в шаблоне транзакции для хранения вещей и определить собственный метод отката в аннотации @Transactional?

Вот как я об этом думаю, я не следую тому, как об этом думает Спринг.Может ли кто-нибудь помочь мне преодолеть разрыв между тем, что я хочу, и тем, как наиболее эффективно это сделать весной?Мне нужно сделать это только для нескольких особых случаев.

Ответы [ 5 ]

7 голосов
/ 16 ноября 2017

Для всех, кто все еще читает это:

Я решил аналогичную проблему с весенними событиями - как предложено Den Roman в варианте 3. Вот основная идея (сценарий вымышленный):

Всякий раз, когда я выполняю внешние операции, которые необходимо откатить вместе с транзакцией, я публикую событие в моем методе @Transactional, используя поддержку из пружины (org.springframework.context.ApplicationEventPublisher):

@Transactional
public String placeOrder(Order order) {
    String orderId = orderServiceGateway.createOrder(order);
    applicationEventPublisher.publishEvent(new OrderCreatedEvent(orderId));
    workflowService.startWorkflow(orderId);
    return orderId;
}

Само событие может быть любым объектом - я создал POJO с подробной информацией об удаленной сущности, которую нужно удалить.

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

@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollBackOrder(OrderCreatedEvent orderCreatedEvent) {
    String orderId = orderCreatedEvent.getOrderId();
    orderServiceGateway.deleteOrder(orderId);
}

Конечно, рекомендуется перехватывать и регистрировать исключение из операции отката, чтобы не потерять исходное исключение из метода placeOrder().

По умолчанию эти события являются синхронными,но они могут быть сделаны асинхронными с помощью дополнительной конфигурации.

Вот очень хорошая статья об этом механизме, включая подробную конфигурацию и pitfalls: Синхронизация транзакций и события приложений Spring (DZone)

Хотя решение мне не нравится на 100%, потому что оно загромождает бизнес-логику вещами публикации событий и привязывается к весне, оно определенновыполняет то, что я от него ожидаю, и позволяет передавать контекст из транзакционного метода в метод отката, который недоступен через традиционный блок try / catch вне транзакционного метода (если вы не поместили свой контекст в само исключение,что не очень приятно).

5 голосов
/ 26 сентября 2013

Я перечитал ваш вопрос несколько раз и не уверен, что полностью понял ваш вопрос. Я предполагаю, что вы выполняете someCode , и если это не удается, вы хотели бы выполнить myCustomRollback , в котором есть некоторая информация о someCode. Поэтому я постараюсь дать общий ответ.

Если вы хотите, чтобы весна откатила какой-то код. Он будет выполнять откат только того, что является rollBackAble, например транзакций jdbc. Предположим, у вас есть метод, который выполняет 2 вызова.

@Transactional
public void doStuff(SomeEntity entity, File file) {
   persist(entity);
   customFileService.createOnFileSystem(file);
   throw new RunTimeException();
}

Таким образом, приведенный выше код всегда будет выполнять откат. Это отменит сохранение вашей сущности, но не создание вашего файла, поскольку он не управляется транзакциями Spring, если только вы не предоставите для него собственную реализацию.

Во-вторых, Spring предоставляет 2 способа работы с транзакциями:

  • Spring AOP: во время выполнения создается прокси , который украсит ваш код транзакционными данными. Если ваш класс будет называться MyClass, то Spring создаст имена классов MyClassProxy, которые обернут ваш код в код транзакции.
  • AspectJ: во время компиляции ваш файл .class будет скорректирован, а код транзакции будет встроен в ваш метод.

Подход аспектаJ кажется сложнее настроить, но он не так уж и прост и его использование намного проще. Поскольку все, что помечено @Transactional, будет встроено (сплетено) с кодом. Для Spring AOP это не так. Например, транзакционные внутренние вызовы методов в Spring будут игнорироваться! Таким образом, aspectJ обеспечивает более интуитивный подход.

Вернуться к тому, что я думаю, ваш вопрос (код всего в 1 классе):

public void doSomeCode() {
    Object restCall = initialize();
    try {
      execute(restCall);
    } catch (CustomException e) {
      myCustomRollback(restCall; e);
    }
}

@Transactional(rollbackFor = CustomException.class)
private void execute(Object restCall) throws CustomException {
    // jdbc calls..
      restCall = callRest(restCall);
      throw new CustomException();
}

void myCustomRollback(Object restCall, CustomException e) {
   ...
}

Приведенный выше код будет работать только с AspectJ! Так как вы делаете внутренние вызовы методов, которые также кажутся приватными! АОП во время выполнения не может справиться с этим.

Итак, все, что происходит (в режиме rollbackAble) при выполнении, будет откатано. А в doStuff у вас есть информация об объектах, которые использовались в execute, теперь вы можете использовать в myCustomRollback для отката ваших REST-компонентов вручную.

Не уверен, правильно ли я ответил на этот вопрос, но надеюсь, что это поможет кому-то с подобной проблемой.

1 голос
/ 16 апреля 2011

Управление транзакциями Spring. Поведение по умолчанию для автоматического отката - для непроверенных исключений

так что для пользовательского исключения,

@Transactional(rollbackFor = CustomException.class, noRollbackFor = RuntimeException.class)
public void doSomething(...
)

транзакция будет отменена, если существует исключение, соответствующее указанному. Если исключение не совпадает, оно передается вызывающей стороне службы или оболочке TransactionRolledBackException

если вы используете org.springframework.transaction.PlatformTransactionManager, это более управляемая обработка исключений, чем шаблон

проверить документацию http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html

0 голосов
/ 26 февраля 2016

1 решение заключается в реализации собственного менеджера транзакций путем расширения одного

2 решение - использование класса TransactionSynchronizationManager

3 решение - использование @TransactionalEventListener в случае наличия Spring 4

0 голосов
/ 16 апреля 2011

вы можете использовать совет AfterThrowing (когда выдается исключение) и вызывать там свой метод (myCustmRollback()), вы можете использовать класс TransactionSynchronizationManager, чтобы получить текущую транзакцию и откатить ее ...

в качестве альтернативы .. вы можете использовать AroundAdvice, чтобы начать и зафиксировать / откатить вашу транзакцию (таким образом вы можете использовать предоставленный пружиной диспетчер транзакций, используя класс TransactionSynchronizationManager)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...