web-dev-qa-db-fra.com

existe-t-il un moyen de forcer une annulation transactionnelle sans rencontrer d'exception?

J'ai une méthode qui fait un tas de choses; parmi eux faisant un certain nombre d'inserts et de mises à jour. C'est déclaré comme ça ...

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

Cela fonctionne exactement comme il est supposé et cela ne me pose aucun problème. Il existe cependant des situations où je veux forcer le retour en arrière malgré l'absence d'une exception ... pour le moment, je force une exception lorsque je rencontre les bonnes conditions, mais c'est moche et je ne l'aime pas.

Puis-je appeler activement la restauration d'une manière ou d'une autre? L'exception l'appelle ... Je pense que je pourrais peut-être aussi.

36
Genia S.

Dans Spring Transactions, vous utilisez TransactionStatus.setRollbackOnly()

Le problème que vous avez ici est que vous utilisez @Transactional pour démarquer vos transactions. Cela présente l'avantage d'être non invasif, mais cela signifie également que si vous souhaitez interagir manuellement avec le contexte de la transaction, vous ne pouvez pas.

Si vous souhaitez contrôler étroitement le statut de votre transaction, vous devez utiliser des transactions programmatiques plutôt que des annotations déclaratives. Cela signifie utiliser le TransactionTemplate de Spring ou utiliser directement PlatformTransactionManager. Voir la section 9.6 du manuel de référence du ressort.

Avec TransactionTemplate, vous fournissez un objet de rappel implémentant TransactionCallback, et le code de ce rappel a accès aux objets TransactionStatus.

Ce n'est pas aussi agréable que @Transactional, mais vous contrôlez de plus près le statut de votre émetteur. 

22
skaffman

Nous n'utilisons pas EJB mais Spring et nous avons choisi l'approche AOP ..__ Nous avons implémenté la nouvelle annotation @TransactionalWithRollback et utilisé AOP pour encapsuler ces méthodes annotées avec des conseils "approximatifs". Pour mettre en œuvre les conseils que nous utilisons, nous avons mentionné TransactionTemplate. Cela signifie un peu de travail au début, mais en conséquence, nous pouvons simplement annoter une méthode avec @TransactionalWithRollback comme nous utilisons @Transactional dans d'autres cas. Le code principal semble propre et simple.

//
// 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>
16
Jakub

Appelez setRollbackOnly() sur la SessionContext si vous êtes dans un EJB.

Vous pouvez injecter SessionContext comme ceci:

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() est un membre de EJBContext. SessionContext extend EJBContext: http://Java.Sun.com/j2ee/1.4/docs/api/javax/ejb/SessionContext.html Notez que ce n'est disponible que dans les EJB de session.

@Resource est une annotation Java EE standard, vous devriez donc probablement vérifier votre configuration dans Eclipse. Voici un exemple de comment injecter le SessionContext en utilisant @Resource.

Je suppose que ce n’est probablement pas votre solution, car il semble que vous ne travailliez peut-être pas avec les EJB - ce qui explique pourquoi Eclipse ne trouve pas @Resource.

Si tel est le cas, vous devrez alors interagir directement avec la transaction - voir Modèle de transaction.

15

Cela fonctionne pour moi:

TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
13

Vous devriez avoir Spring injecter le gestionnaire transactionnel. Ensuite, vous pouvez simplement appeler la méthode de restauration sur celle-ci.

3
Ruggs

J'ai des méthodes de service annotées avec @Transactional. Lorsque la validation échoue et que j'ai déjà une entité attachée à l'unité de travail actuelle, j'utilise sessionFactory.getCurrentSession().evict(entity) pour m'assurer que rien n'est écrit dans la base de données. De cette façon, je n'ai pas besoin de lancer une exception.

2
Urs

Oui, nous pouvons forcer la restauration en utilisant @Transactional (niveau de classe) sans rencontrer d’exception. Nous pouvons simplement lancer une exception (toute autre appropriée). Comme

if(some condition matches){
 throw new DataIntegrityViolationException("Rollback Tnx.. Since ..." );
} 
0
myounism