Подтвердить что ты не робот

Существует ли способ заставить транзакционный откат без возникновения исключения?

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

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

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

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

4b9b3361

Ответ 1

Вызовите 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 extends EJBContext: http://java.sun.com/j2ee/1.4/docs/api/javax/ejb/SessionContext.html Обратите внимание, что это доступно только в сессионных EJB.

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

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

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

Ответ 2

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

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

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

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

Это не так хорошо, как @Transactional, но вы всецело контролируете свой статус tx.

Ответ 3

Мы не используем 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>

Ответ 4

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

TransactionInterceptor.currentTransactionStatus().setRollbackOnly();

Ответ 5

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

Ответ 6

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

Ответ 7

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