Есть ли способ принудительного отката транзакции без исключения? - PullRequest
36 голосов
/ 07 мая 2009

У меня есть метод, который делает кучу вещей; среди них делает ряд вставок и обновлений. Заявлено так ...

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
public int saveAll(){
 //do stuff;
}

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

Можно ли как-нибудь активно отозвать откат? Исключение называет это ... Я думаю, может быть, я тоже могу.

Ответы [ 8 ]

22 голосов
/ 07 мая 2009

В весенних транзакциях вы используете TransactionStatus.setRollbackOnly().

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

Если вы хотите строго контролировать свой статус транзакции, вы должны использовать программные транзакции, а не декларативные аннотации. Это означает использование Spring TransactionTemplate или непосредственное использование его PlatformTransactionManager. См. Раздел 9.6 справочного руководства Spring.

С TransactionTemplate вы предоставляете объект обратного вызова, который реализует TransactionCallback, и код в этом обратном вызове имеет доступ к объектам TransactionStatus.

Это не так хорошо, как @Transactional, но вы получаете более точный контроль над своим статусом передачи.

16 голосов
/ 12 марта 2013

Мы не используем EJB, а просто Spring, и мы выбрали подход AOP. Мы внедрили новую аннотацию @TransactionalWithRollback и использовали AOP, чтобы обернуть эти аннотированные методы советом «вокруг». Для реализации совета мы используем упомянутый TransactionTemplate. Это означает небольшую работу в начале, но в результате мы можем просто аннотировать метод с помощью @TransactionalWithRollback, как мы используем @Transactional в других случаях. Основной код выглядит простым и понятным.

//
// Service class - looks nice
//
class MyServiceImpl implements MyService {
    @TransactionalWithRollback
    public int serviceMethod {
        // DO "read only" WORK
    }
}

//
// Annotation definition
//
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface TransactionalWithRollback {
}

//
// the around advice implementation
//
public class TransactionalWithRollbackInterceptor {
    private TransactionTemplate txTemplate;
    @Autowired private void setTransactionManager(PlatformTransactionManager txMan) {
        txTemplate = new TransactionTemplate(txMan);
    }

    public Object doInTransactionWithRollback(final ProceedingJoinPoint pjp) throws Throwable {
        return txTemplate.execute(new TransactionCallback<Object>() {
            @Override public Object doInTransaction(TransactionStatus status) {
                status.setRollbackOnly();
                try {
                    return pjp.proceed();
                } catch(RuntimeException e) {
                    throw e;
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
}

//
// snippet from applicationContext.xml:
//
<bean id="txWithRollbackInterceptor" class="net.gmc.planner.aop.TransactionalWithRollbackInterceptor" />

<aop:config>
    <aop:aspect id="txWithRollbackAspect" ref="txWithRollbackInterceptor">
        <aop:pointcut 
            id="servicesWithTxWithRollbackAnnotation" 
            expression="execution( * org.projectx..*.*(..) ) and @annotation(org.projectx.aop.TransactionalWithRollback)"/>
        <aop:around method="doInTransactionWithRollback" pointcut-ref="servicesWithTxWithRollbackAnnotation"/>
    </aop:aspect>
</aop:config>
15 голосов
/ 07 мая 2009

Позвоните setRollbackOnly() на SessionContext, если вы находитесь в EJB.

Вы можете ввести SessionContext так:

public MyClass {
    @Resource
    private SessionContext sessionContext;

    @Transactional(propagation = Propagation.REQUIRED, 
                   isolation = Isolation.DEFAULT, 
                   readOnly = false)
    public int saveAll(){
        //do stuff;
        if(oops == true) {
             sessionContext.setRollbackOnly();
             return;
        }
    }

setRollbackOnly() является членом EJBContext. SessionContext расширяет EJBContext: http://java.sun.com/j2ee/1.4/docs/api/javax/ejb/SessionContext.html Обратите внимание, что он доступен только в сессиях EJB.

@Resource - это стандартная аннотация Java EE, поэтому вам, вероятно, следует проверить свои настройки в Eclipse. Вот пример того, как внедрить SessionContext, используя @Resource.

Я подозреваю, что это, вероятно, не ваше решение, поскольку кажется, что вы, возможно, не работаете с EJB - объясняя, почему Eclipse не находит @Resource.

В таком случае вам нужно будет напрямую взаимодействовать с транзакцией - см. Шаблон транзакции.

14 голосов
/ 06 мая 2014

Это работает для меня:

TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
3 голосов
/ 07 мая 2009

у вас должна быть весна, чтобы внедрить менеджер транзакций. Затем вы можете просто вызвать метод отката для него.

2 голосов
/ 18 ноября 2011

У меня есть методы обслуживания, помеченные @Transactional. Когда проверка не проходит, и у меня уже есть объект, присоединенный к текущей единице работы, я использую sessionFactory.getCurrentSession().evict(entity), чтобы убедиться, что ничего не записано в базу данных. Таким образом, мне не нужно создавать исключение.

0 голосов
/ 07 октября 2018

Да, мы можем принудительно выполнить откат при использовании @Transactional (уровень класса) без исключения. Мы можем просто выбросить исключение (любое подходящее). Как

if(some condition matches){
 throw new DataIntegrityViolationException("Rollback Tnx.. Since ..." );
} 
0 голосов
/ 22 июня 2009

Создайте исключение и используйте платформу, как задумано, иначе не используйте декларативное управление транзакциями и следуйте советам Скаффмана выше. Сохраняй это простым.

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