Существует ли способ заставить транзакционный откат без возникновения исключения?
У меня есть метод, который делает кучу вещей; среди которых выполняется ряд вставок и обновлений. Он объявил это...
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
public int saveAll(){
//do stuff;
}
Он работает точно так, как предполагается, и у меня нет проблем с ним. Однако есть ситуации, когда я хочу заставить откат, несмотря на отсутствие исключения... на данный момент я заставляю исключение, когда сталкиваюсь с правильными условиями, но это уродливо, и мне это не нравится.
Могу ли я как-то активно назвать откат? Исключение вызывает это... Я думаю, может быть, я тоже могу.
Ответы
Ответ 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
Выбросьте исключение и используйте фреймворк, как он был разработан иначе, не используйте декларативное управление транзакциями и следуйте советам выше. Держите его простым.